solid / notifications

Solid Notifications Technical Reports
https://solid.github.io/notifications/protocol
MIT License
11 stars 7 forks source link

Define unsubscribing #145

Open elf-pavlik opened 1 year ago

elf-pavlik commented 1 year ago

Currently Notification Protocol only defines Subscription. WebhookSubscription2021 defines unsubscribing, IMO it should also be defined on the protocol level so that it can be used with any channel type.

I see few aspects that need to be clarified

Including unsubscribe details in subscription response

This seems reasonable for notification channel description to provide details needed to unsubscribe It makes sense that authentication should use same identity as subscription client which made subscription request.

Including unsubscribe details in each notification

WebhookSubscription2021 also requires that unsubscribe details are included in every notification. Besides that it posts a question in Further considerations

The fact that the Pod allows any URI to be submitted as a target might lead to distributed denial of service attacks originating from Pods. A malicious actor could create an app and trick many users to join it. The malicious app would then create a webhook targeting its desired target. It may be a good idea to build in an automated verification process to confirm that the entity sending the subscription request also owns the target webhook endpoint.

Given above it might make sense to use Capability URL in each notification and don't require authentication on it. This way one can at least unsubscribe from notifications where malicious actor made the subscription.

/cc @jaxoncreed

CxRes commented 1 year ago

Unsubscribe must be handled in the main protocol. I believe it is handled consistently as a subcase of subscription modification, as discussed in #103 and further in meetings.

I propose that we allow clients to POST a partial of Notification Channel Data Model, that is, only the property the clients wants to change to the Subscription Service. In case we wish to unsubscribe, this will simply be an id and endTime:

{
  "@context": [
    "https://www.w3.org/ns/solid/notifications/v1"
  ],
  "id": "https://channel.example/ac748712",
  "endAt": "time:Instant"
}

It can alternatively be a PUT with the above message or even a DELETE on the created subscription endpoint in case the Subscription Service is a container.

This will take away the need to add unsubscribe information in the subscription response or message!

CxRes commented 1 year ago

There is one other complication involved, which I had raised in a discussion two years ago https://gitter.im/solid/specification?at=5ea2b8c6501f8f72a5ffa61a. Some clients might want to continue to watch resources that have been deleted, in the expectation that they will be created again, other times we want to stop watching. I believe the proposal I make above is suitable to handle both these cases.

Aside: I would really like this to be handled as a feature, where the client can specify the behaviour beforehand rather than closing or staying after the deletion.

jaxoncreed commented 1 year ago

Another feature to consider: Subscribers should be able to request a list of documents that they are subscribed to.

jaxoncreed commented 1 year ago

I propose that we allow clients to POST a partial of Notification Channel Data Model, that is, only the property the clients wants to change to the Subscription Service. In case we wish to unsubscribe, this will simply be an id and endTime:

I don't see any problems with this solution.

CxRes commented 1 year ago

Another feature to consider: Subscribers should be able to request a list of documents that they are subscribed to.

Do you mean: a. List of Topic Resources or b. List of Subscriptions/Channels that a particular client can connect/is connected to.

Both, are use useful features. Depending on your answer, we can open new issue/s?

CxRes commented 1 year ago

I don't see any problems with this solution.

From what I understood from our discussion, the question is if we want to use POST or DELETE?

POST has the benefit of specifying a later end time.

DELETE on the other hand, is more intuitive, if we think of Subscription Service as a container and each Subscription as a Resource. (There are other benefits for the treating the Service as a container, e.g. one can GET on the container to solve (b.) above)

I see no reason why we cannot support both?

csarven commented 1 year ago

Unsubscribing can indeed work with requesting to update (POST) the notification channel or removing it (DELETE).

There needs to be a clarification on the constraints on the notification channel resource ( https://github.com/solid/notifications/issues/158 ) - the kind of IRI.

If the notification channel is not an HTTP URI, DELETE is not possible. POST targeting a subscription service can work with a IRI for notification channel in the payload.

Subscription service needs to be able to respond with a 201 with Location header referring to the notification channel resource in order to be able to later DELETE. However, subscription service is not required to be a container. It just has similar behaviour that can work with some existing implementations ( https://github.com/solid/notifications/issues/36 ).

The payload of POST targetting a subscription service does not currently have a meaning on the value of the id property ( https://solid.github.io/notifications/protocol#notification-channel-data-model ). Which BTW needs an update because as it reads right now by following https://solid.github.io/notifications/protocol#subscription , the notification-channel-data-model is suitable for the response but not the request, i.e., as if the request should include notification channel id, and obviously that's not meaningful.

As it stands, the specification is effectively saying, no id means create/issue notification channel, and id means update (if exists) in the subscription request. That needs to be a bit more clear in the spec.

That approach can work but I stress again that we should be careful about this model.

Not too long ago we were looking into SubscriptionRequest: https://github.com/solid/notifications-panel/blob/main/meetings/2022-12-15.md (see also https://github.com/solid/notifications/issues/62#issuecomment-1332663243 ). So, normally there'd be a Request activity and an Update activity in the payload of POST - irrespective of the vocabulary considerations.

Lastly, as it stands I don't believe "endAt": "time:Instant" will work since the value is expected to be in xsd:dateTime datatype.

CxRes commented 1 year ago

I stress again that we should be careful about this model. Care to elabourate how we are not being careful, or what is missing from the consideration? I had proposed POST and PUT/DELETE as two separate options depending on whether we exposed the subscription endpoint (what you are calling notification channel resource (we need to bikeshed this)). For a 0.2.1/0.3.0 any of these mechanisms is OK.

Re id: I see no problem in the Client requesting the use of an id for a first request. If a server is already using or otherwise does not want to create said id, it can refuse the subscription request or create another id. If no id is provided, a server will always create one for you.

Actually, I would like an explicit URI per id because it also allows one to do a GET to get subscription parameters, list of topics (see @jaxoncreed's additional request above) or even audit more deeply (even if that URI isn't a contained resource). Doing an effective GET on subscription service using a POST is weird.

elf-pavlik commented 1 year ago

I see no reason why we cannot support both?

Do you mean that server would have MUST on both or either one? If the server can choose than the client must know which one to use. Probably this would need to be included in the Notification Channel description, as well as in every notification delivered to sendTo.

CxRes commented 1 year ago

Do you mean that server would have MUST on both or either one?

I meant that Subscription Servers support, as MUST, either POST (on Subscription Service) or PUT/DELETE/GET (on Subscription ID) or both. Though, I am starting to like option 2 more.

Servers don't get to choose, one can do it, but that makes no sense!

csarven commented 1 year ago

The interaction should be simple (do and undo the same way): if POST is used to request a subscription, it should be used to unsubscribe or update the subscription. This is where improving the data model model helps. (And why I kept stressing considering AS2 like notions/activities to make a SubscriptionRequest so that it be can be Undo or UnSubscribe or whatever.)

I do not think that a POST payload with id or no id is adequately expresses the intended processing of the request.

As it stands, irrespective to HTTP method, payload with no id (in JSON-LD) would be interpreted as a blank node, which conflicts with processing based on the current JSON-LD context which expects an IRI.

PUT and DELETE to create, update and delete a notification channel resource adds more complication:

I can see some use for DELETE on notification channel resource, but not PUT.

Allowing GET on notification channel resource would be useful as mentioned in, e.g., https://github.com/solid/notifications/issues/111 , https://github.com/solid/notifications/pull/29#issuecomment-1009840586 (where contained resources entail available notification channels in today's language) , and surely elsewhere. But again, for that to be possible, notification channel resource needs to be constrained to HTTP URI (where it is currently IRI). And, it is especially a non-starter for any subscription service to even think about optionally allow PUT or DELETE on notification channel resource. See issue 158.

CxRes commented 1 year ago

The interaction should be simple (do and undo the same way): if POST is used to request a subscription, it should be used to unsubscribe or update the subscription.

I find this argument especially strange. I could, by that logic, argue why have PUT and DELETE at all in HTTP.

PUT and DELETE to create, update and delete a notification channel resource adds more complication:

  • it requires the client to have prior or out-of-band knowledge about the URI space in which a notification channel can be created with PUT. It needs additional dependencies on other specs for this to work properly.

I have never suggested using PUT to create a notification channel. Notification channels will still be created using POST (as you rightly point out, only server knows its URI space; however, I might add, clients can always request a URI). Only updates and unsubscribe will work with PUT and DELETE. This is a common REST pattern.

  • it requires additional request semantics to subscribe (in addition to POST) where only one suffices for interop.

HTTP allows more than one way of acting on resources for good reason. We should embrace that. Minimalism adds complexity burden elsewhere (such as overloading POST).

But again, for that to be possible, notification channel resource needs to be constrained to HTTP URI.

True! But if we switch to Subscription Service becoming a container, this is going to happen anyway! GET benefits are a bonus!!!

csarven commented 1 year ago

PUT and DELETE doesn't just "work" for unsubscribing out of the box without relying on the request semantics given by the specification (as opposed to in-band in HTTP). In LDP, there are notions for container and membership, as well as whether a resource participates in LDP (as a type of for instance). The Solid Notifications Protocol doesn't have anything about the notions of "subscribing" and "unsubscribing" in HTTP, even if we were to say subscription service is a container. So, while the intended effect of something like PUT is to both update the notification channel resource and to update the subscription state. That "update the subscription state" is not communicated in PUT whatsoever. That's entirely out of band / coming from the specification. So, to do PUT properly, we'll need to add more information into the request so that a server can interpret the meaning, and follow up on the side-effects of replacing the representation of the resource as targeted by the HTTP request. Ditto DELETE.

I'm not saying this is impossible. I'm contrasting the solutions, but most importantly, it should be clear abundantly clear why an alternative approach is needed. Like I said, I can see some cases with DELETE, e.g., to allow a SubscriptionClient to remove a notification channel from record, and effectively 'unsubscribing', but even still I'm not sure if that's in high demand right now. If a SubscriptionService wants to allow that (Allow: PUT, DELETE) it can do that if it wants to, but it doesn't need to be for minimum for interop, given:

The logic of POST in simple(r) here and it is to process some instructions (bar the payload is clear.) See for instance AS2 vocabulary where some activities can be undone / negated. If POST is used to subscribe (e.g., notify:Subsribe), its undo would be unsubscribe (e.g., as:Undo or notify:Unsubscribe if AS2 is not a possibility here).

As for SubscriptionService as a container, I'm still on the fence on that. See dedicated issue. I can see it being useful, but I'm not sure if again that's something that needs to be in place for stuff to work.

CxRes commented 1 year ago

One thing that I am not sure about is how strong is the idempotence requirement of PUT. Does a PUT body completely replace the representation of a resource, or is it merely that repeating the same message body should not change the resource representation (I am unable to discern this from RFC 2616 section 9.6). If it's the former then PUT is not viable as it requires the notification channel to be respecified even if updating a single feature, but if it is the latter then extending the spec to support this is feasible. Yes, we will have to define what the representation of a notification channel resource means and what the update semantics look like, but it is doable. Further, we can add this (PUT/DELETE to update/unsubscribe, not update/unsubscribe) in at a later stage, say a v0.4.

As you can see from my original suggestion, I am not opposed to supporting POST at all. I am also asking that we evaluate the PUT/DELETE option carefully before taking a call.


but even still I'm not sure if that's in high demand right now.

but I'm not sure if again that's something that needs to be in place for stuff to work.

I feel "demand" should not be the criterion, but completeness should be. The complexity of the protocol needs to be balanced against the developer complexity (both server and client), rather than striving for minimal interop. The obligation should be on us to put in the work, not our users.

csarven commented 1 year ago

When there is no strong demand to complicate the specification in the presence of a simpler solution, striving for hypothetical "completeness" as a principle achieves what exactly?

Can you update the table in https://github.com/solid/notifications/issues/141 with the product(s) you or others are implementing or committing to implement so that we improve our sense of concrete development?


https://solid.github.io/notifications/protocol refers to RFC 7231 in a number of places. (Yes, we can move to RFC 9110 but that's not critical here). As mentioned, PUT will replace the notification channel resource state, and the affects pertaining to unsubscribing or modifying the subscription would need to be additionally defined, e.g., typically introducing a new resource type like solid:NotificationChannel and advertising that on the Link header and so forth.

Let me know how you want to balance or measure (whether specification or development) complexity considering the following options (whether required or optional):

POST + data model for subscribing/unsubscribing/modifying subscriptions

vs.

POST + PUT + DELETE (specifying request semantics on notification channel and affects) + data model for subscribing/unsubscribing/modifying subscriptions.


We'd get a lot more mileage by fixing the data model so that POST works properly.

CxRes commented 1 year ago

Regarding Completeness: (Unfortunately, we cannot have two separate threads, for two things, i.e. What and How, are getting mixed up. Or maybe I am misunderstanding you. But, I do not appreciate the tenor of the question). The spec is incomplete without a modify/unsubscribe. Heck, it is incomplete without a way for the client to check the channel configuration/state. This is what I meant by completeness. This requirement is NOT hypothetical!

On the other hand, the “demand” you are talking about even now is entirely speculative. Besides, it may be some time before a critical mass might use this spec, only then can we have a fair gauge of demand. Even in the absence of demand or proof of product implementation (which is indicative of measurability bias), these features need to be adequately covered by the spec to be considered complete (we can reason this theoretically). But handing the world an incomplete spec is a sure-fire way to ensure that a critical mass is not reached.

Let me know how you want to balance or measure (whether specification or development) complexity considering the following options (whether required or optional):

I want to see if POST + PUT + DELETE creates a simpler mental model and thus is simpler and maybe even safer to implement than POST. POST overloads the Subscription Service, not just across operations, but also across multiple connections. I fear this might complicate subscription management for both client and server. While I understand it is not our job to idiot-proof for implementors, it also creates a greater possibility of subtle bugs where information might be leaked across different connections, if the implementer makes a mistake.

We'd get a lot more mileage by fixing the data model so that POST works properly.

Having said all that, again I reiterate, I am not opposed to POST as an interim or even a permanent solution. All I am asking is to keep an open mind about doing things two ways.

csarven commented 1 year ago

Or maybe I am misunderstanding you.

I believe you're.

The context of everything I wrote was literally about specific solutions, namely PUT and DELETE in contrast to POST. It wasn't about whether unsubscribing or modifying existing subscriptions is needed. See also red herring.

Let me be abundantly clear. There is no dispute (at least from me) about having a solution for unsubscribing or modifying an existing subscription. I've written and discussed about that elsewhere. I can assure you that it is not news or a ground breaking proposal to me. I'm glad that we are (as a group) more or less on the same page about figuring something out towards that. See also what I wrote in https://github.com/solid/notifications-panel/issues/1#issue-489650894 in 2019:

Ability to unsubscribe from particular activities.

In the very first issue of that repository (and even earlier elsewhere)!

So, please +1 my idea/proposal ;)

I gave sufficient detail about the suitability of particular solutions, i.e., PUT/DELETE towards unsubscribing/modifying. When I say they are not really in demand or are more complex approaches, I am trying to work with the data in front of me. I've shown examples of how POST can be used, what the gaps are (re data model), as well as how similar needs are addressed in near by specs (AS2). PUT/DELETE in contrast is not as straight-forward. A bunch of other things need to be put in place for it to work both on spec level as well as additional development, and hence more friction. I'm asking you to make a development commitment and share from your experience so that you can indeed see what's a simple "mental model" for yourself. It isn't going to appear out of thin air.

I don't believe POST "overloads" the Subscription Service whatsoever towards subscribing/unsubscribing/modifying. But you are welcome to convince or correct me with data or a strong argument (with references to specs).

As I see it, the SubscriptionService already allows POST and processes the payload. In a nutshell, the affect is going to come down to a couple of if-statements or a switch-statement to check the type of the payload (graph), and continue with a specific process (to subscribe/unsubscribe/modify).

I am not developing a SubscriptionService at this time (but I just might to tick more boxes). I can with good confidence say that the mental model with PUT+DELETE will require more work. DELETE may not even be suitable to unsubscribe because we don't necessarily want subscriptions marked to be inactive/dead to be deleted, or managed as far as resources go by a client. If a server wants to do house cleaning and remove a resource (404/410), it can do that as the side-effect of unsubscribing, which can be noted or detailed in the spec. Delete along does not communicate unsubscribe. Sending a self-descriptive payload would, just as in theory the payload in the request communicates "subscribe". Same goes for modifying/altering the details of a subscription if so needed. Let me just say that PATCH would even be more suitable than PUT to modify an existing subscription. And, we haven't even discussed what modifying an inactive subscription would be like with PUT. I still don't think PUT/DELETE is a good path to take here.

I said enough and so I'll stop for now. If folks want to commit to developing a particular solution, please say so here, and update the table in the call for implementations. Or offer some new information that we can work with.

CxRes commented 1 year ago

(I am not arguing the merits of POST vs POST + PATCH + GET (as per the latest iteration of the idea) in this comment. I am happy to wait for other commenters to respond or PR).

Red Herring

Woah! Slow down...

You asked me:

striving for hypothetical "completeness" as a principle achieves what exactly?

There is a serious deficiency in the way we are operating. Good systems engineering practice requires that requirements precede specifications (See any INCOSE literature). By not drawing up UCR first (comments in issues don't qualify), I am afraid that we do not have an adequate/robust understanding of all the ways in which channels need to be managed (And even if we have, we cannot be confident of it). This has literally resulted in us rewriting the spec over the new year.

Here, in response to your comment, I pointed to an extra requirement that has been identified, that of, querying the notification channel (something we had missed). You have previously identified an auditing requirement as well. My worry is that POST might not be adequate to address these incompletely identified requirements. It is certainly not an elegant solution. In choosing between the two solutions, it might be very well the case that we are committing to completeness or lack of thereof. These are different things, but not separable things.

Maybe I was overloading my comment in service of a more general point, that "demand" is not a good criterion for feature selection. A demand might crop up in the future, that we might not be able to accommodate because of choices we make here. Or we will have to add another solution just to meet that demand and complicate the spec. By thinking about "completeness", we can minimize the incidence of such events in the first place.


I can assure you that it is not news or a ground breaking proposal to me.

So how come modify/subscribe has not been included in the spec, and in how many years now?

That doesn't sound nice now, does it? Even if you think I am wrong, I do not appreciate the rhetorical tone used in the comments above. I am certainly not as knowledgable as you, for this is not my field even, I find myself here accidentally. I may not even be as intelligent as you, but it is not as if I have made my comment without giving it some thought. I am looking to learn/solve stuff, not to argue for argument's sake as I am being accused of in the comment thereafter!

Rhetorical point aside, we are still debating things that should have gone into the first version of the spec more than a year later. That worries me!


I'm asking you to make a development commitment and share from your experience so that you can indeed see what's a simple "mental model" for yourself. It isn't going to appear out of thin air.

Sorry, that is a bias to empiricism. Ideas do come out of “thin air”. It is called a hypothesis. Sure, you need to test if your hypothesis is correct experimentally. In many cases, though, even this can be done through a thought experiment at a first instance.

Just because I cannot make a development commitment (I may or may not be able to apply the spec within the timeframe of this panel) to implement the new spec should not disqualify me from foreseeing deficiencies with the spec and make specific proposals for it.

By the way, some of us here are volunteering on our own dime.


I don't believe POST "overloads" the Subscription Service whatsoever towards subscribing/unsubscribing/modifying.

POST on Subscription Service to perform multiple operations based on different message bodies is a textbook example of operator overloading. Not that operator overloading is inherently bad.

Further, that you are performing operations for different notification channels on the same URI is yet another form of polymorphism. All hail the mighty Subscription Service :P

csarven commented 1 year ago

I agree, let's take it down a notch. I will respond to some of your comments but hope that we can continue the tangent discussion elsewhere. Perhaps in a meeting where we have more social bandwidth.

Your and everyone's contributions are invaluable and welcome in whatever shape or form that you are willing. Pardon me if my wording came across as if no commitment to implementing doesn't get you or anyone a voice. I can only assure you that was not my intention. I took everything you've said seriously and responded as best as I could but perhaps not enough.

There is indeed a lot of re-writing but that is a natural part of the authoring and editorial process, as well as looping in implementation feedback. Let's not forget that the specs are currently in 0.x versions and so it is okay that we revisit things as we identify gaps and look for improvements. Updating the UCR document, as well as having early implementations all feed towards that. So, I'd argue that we've been doing pretty well all things considered.

While the initial UCR document is indeed flawed (read: solutions engineering, https://github.com/solid/notifications-panel/issues/76 ) it served as orientation for us. It needs an update. Similarly some of the UCs in the UCR document provide more detail to the UCs in https://github.com/solid/notifications-panel/issues/1#issue-489650894 , and the Notifications Protocol has a solution to some of those UCs. There are other UCs in the repos and discussions, I'm just using that as example.

As I see it, we just didn't get to modifying/unsubscribing. In the same way we didn't get to many other UCs or features. The fact that we are discussing them in more detail now and have consensus on the needs is a good signal, and so can incorporate them into the UCR and the Notification Protocol. Ideally having the UCR up front would be great, but in my experience we don't always have that vantage point. Different stakeholders/contributors jumping in at different times. But, we can't indefinitely revise either. There is scope, and "due dates", and so I can only stress milestones/ETAs, check in on commitments, and so forth, to get a sense of what we have and how well we can meet our goals.

elf-pavlik commented 1 year ago

Including unsubscribe details in each notification

WebhookSubscription2021 also requires that unsubscribe details are included in every notification. Besides that it posts a question in Further considerations

EDIT: This is only relevant to sendTo channels, since receiveFrom can simply disconnect and will not receive unwanted notifications.

Keeping in mind #155 I think we could make an exception in this case and make the Capability URL implicit, given that we could simply add `unsubscribeAt':

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://www.w3.org/ns/solid/notification/v1"
  ],
  "id": "urn:uuid:fc8b5af4-bd7e-4fd1-a649-afcbd0e1c083",
  "type": "Update",
  "object": "https://example.org/guinan/profile",
  "state": "128f-MtYev",
  "published": "2021-08-05T01:01:49.550Z",
  "unsubscribeAt": "https://channel.example/ac748712"
}

I think in this case simple HTTP DELETE would make it very easy to unsubscribe. Otherwise we would need to provide all the information needed to do the HTTP POST.

elf-pavlik commented 1 year ago

For the record, CSS implements unsubscribing via DELETE on the channel IRI. It doesn't require authentication.

https://communitysolidserver.github.io/CommunitySolidServer/6.x/usage/notifications/#unsubscribing-from-a-notification-channel