diff --git a/README.md b/README.md index 6ef632a..3582847 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ To see the most recent HTML rendered version of the specification from this repo - [`authn-saml`](lws10-authn-saml/): SAML 2.0 Authentication Suite - [`authn-ssi-cid`](lws10-authn-ssi-cid): Self-signed Controlled Identifier Authentication Suite - [`authn-ssi-did-key`](lws10-authn-ssi-did-key): Self-signed `did:key` Authentication Suite +- [`notifications`](lws10-notifications/): Notifications ## Contribution Guidelines: diff --git a/lws10-core/Discovery.html b/lws10-core/Discovery.html index 18f07bb..7b77470 100644 --- a/lws10-core/Discovery.html +++ b/lws10-core/Discovery.html @@ -125,7 +125,8 @@

Storage Description Representation

], "service": [{ "type": "NotificationService", - "serviceEndpoint": "https://storage.example/notification/api" + "serviceEndpoint": "https://storage.example/notification/api", + "subscriptionType": ["WebhookSubscription"] }, { "type": "TypeIndexService", "serviceEndpoint": "https://storage.example/types/api" diff --git a/lws10-core/Unstable-Features/Notifications.md b/lws10-core/Unstable-Features/Notifications.md index 76a99f3..0a6198d 100644 --- a/lws10-core/Unstable-Features/Notifications.md +++ b/lws10-core/Unstable-Features/Notifications.md @@ -1,2 +1,5 @@ -this left intentionally blank +# Notifications +The [LWS Notifications](../../lws10-notifications/index.html) specification extends the Linked Web Storage (LWS) protocol with a mechanism for clients to receive timely updates about changes to resources via Webhooks — server-to-server push notifications with HTTP Message Signatures. + +The notification data model is based on Activity Streams and is discoverable via the storage description resource. diff --git a/lws10-notifications/index.html b/lws10-notifications/index.html new file mode 100644 index 0000000..0b83a96 --- /dev/null +++ b/lws10-notifications/index.html @@ -0,0 +1,921 @@ + + + + + LWS Notifications + + + + +
+

+ This document extends the Linked Web Storage (LWS) protocol with a mechanism for clients + to receive timely updates about changes to resources via Webhooks. The specification + defines a common notification data model and is discoverable via the storage description + resource. +

+
+
+

+ This is an unofficial proposal. +

+
+ +
+

Introduction

+ +

+ The [[!LWS-PROTOCOL|Linked Web Storage]] protocol defines a resource-centric storage + system built on HTTP. While the core protocol supports creating, reading, updating, and + deleting resources, it does not define a mechanism for clients to learn about changes + without polling. +

+ +

+ This specification extends LWS with a notification mechanism that allows clients + to subscribe to resource changes and receive updates via Webhooks — a + server-to-server mechanism whereby the LWS storage server delivers change events to a + registered endpoint via HTTP POST. +

+
+ +
+ +
+

Terminology

+ +

+ The terms "storage description resource", "resource manager", "requesting agent", + "container", and "data resource" are defined by [[!LWS-PROTOCOL]]. +

+ +

+ The terms "controlled identifier document", "verification method", and "verification + relationship" are defined by Controlled Identifiers 1.0 [[!CID-1.0]]. +

+ +

+ This specification defines the following additional terms: +

+ + +
+ +
+

Discovery

+ +

+ A storage MAY support notifications. A storage that supports notifications MUST + advertise a service object in the service array of its storage description + resource with type equal to NotificationService. +

+ +

+ The service object MUST include a serviceEndpoint property whose value + is the URI of the subscription endpoint. The service object MUST include a + subscriptionType property whose value is an array of strings, each + identifying a supported subscription type. +

+ +

+ Additional properties MAY be present on the service object. +

+ +
+{
+  "@context": ["https://www.w3.org/ns/lws/v1"],
+  "id": "https://storage.example/",
+  "type": "Storage",
+  "service": [{
+    "type": "NotificationService",
+    "serviceEndpoint": "https://notification.example/subscriptions",
+    "subscriptionType": [
+      "WebhookSubscription"
+    ]
+  }]
+}
+      
+ +

+ A server is not required to support all subscription types. The + subscriptionType array advertises exactly which types are available. +

+
+ +
+

Notification Envelope

+ +

+ A notification is represented as a JSON-LD document with a + Notification type. The envelope carries metadata about the delivery + context and wraps an Activity Streams 2.0 [[ACTIVITYSTREAMS-CORE]] [[ACTIVITYSTREAMS-VOCABULARY]] + activity describing the event. +

+ +
+

Envelope Properties

+ +

+ A notification envelope has the following properties: +

+ + + +
+{
+  "@context": [
+    "https://www.w3.org/ns/lws/v1",
+    "https://www.w3.org/ns/activitystreams"
+  ],
+  "type": "Notification",
+  "storage": "https://storage.example/",
+  "activity": {
+    "id": "ec4dcc48-6581-4232-81c9-584525a98693",
+    "type": ["Delete"],
+    "object": {
+      "id": "https://storage.example/alice/notes/shopping.txt",
+      "type": ["DataResource"]
+    },
+    "origin": "https://storage.example/alice/notes/",
+    "published": "2026-03-26T10:30:00Z"
+  }
+}
+        
+
+ +
+

Activity Properties

+ +

+ Each activity object within the activity property MUST conform to the + Activity Streams 2.0 data model and MUST include the following properties: +

+ + + +

+ The following properties are OPTIONAL: +

+ + +
+ +
+

Activity Types

+ +

+ A server MUST support the following Activity Streams 2.0 activity types to + indicate LWS resource changes: +

+ + + +

+ Other activity types MAY also be supported. +

+
+ +
+

Batching Notifications

+ +

+ A server MAY combine multiple activities into a single notification envelope by + providing an array of activity objects as the value of the activity + property. +

+ +
+{
+  "@context": [
+    "https://www.w3.org/ns/lws/v1",
+    "https://www.w3.org/ns/activitystreams"
+  ],
+  "type": "Notification",
+  "storage": "https://storage.example/",
+  "activity": [
+    {
+      "id": "a1b2c3d4-5678-9abc-def0-1234567890ab",
+      "type": ["Create"],
+      "object": {
+        "id": "https://storage.example/alice/notes/meeting.txt",
+        "type": ["DataResource"]
+      },
+      "target": "https://storage.example/alice/notes/",
+      "published": "2026-03-26T10:30:00Z"
+    },
+    {
+      "id": "b2c3d4e5-6789-abcd-ef01-234567890abc",
+      "type": ["Update"],
+      "object": {
+        "id": "https://storage.example/alice/profile",
+        "type": ["DataResource"]
+      },
+      "published": "2026-03-26T10:30:01Z"
+    }
+  ]
+}
+        
+
+
+ +
+

Subscriptions

+ +

+ To receive notifications, a subscriber creates a subscription by sending + an authenticated POST request to the serviceEndpoint of the + NotificationService. The request body MUST conform to the + application/lws+json media type. +

+ +
+

Subscription Request

+ +

+ A subscription request MUST contain the following fields: +

+ + + +

+ A subscription request MAY contain additional fields as required by the specific + subscription type. +

+ +
+POST /subscriptions HTTP/2
+Host: notification.example
+Authorization: Bearer <access-token>
+Content-Type: application/lws+json
+
+{
+  "@context": ["https://www.w3.org/ns/lws/v1"],
+  "type": "WebhookSubscription",
+  "topic": [
+    "https://storage.example/alice/notes/",
+    "https://storage.example/alice/profile"
+  ],
+  "inbox": "https://receiver.example/hooks/lws"
+}
+        
+
+ +
+

Subscription Scope

+ +

+ A subscription to a container is recursive: the subscriber receives + notifications for the container itself and for all resources transitively contained + in that container. A subscription to a data resource applies only to that + individual resource. +

+
+ +
+

Subscription Authorization

+ +

+ A server MUST enforce resource authorization when creating a subscription. + If a subscriber does not have the equivalent of read access to all resources + listed in the topic array, the server MUST reject the subscription + request. +

+ +

+ Authorization MUST also be enforced at notification delivery time. A server MUST + NOT deliver a notification about a resource that the subscriber is + not authorized to read at the time the event occurs. This applies to all resources + within the scope of a subscription, including resources in subcontainers. +

+ +

+ If the subscriber's access to a resource or container within the scope of a + subscription is revoked after the subscription is created, the server + MUST stop delivering notifications for the affected resources. The server SHOULD + NOT terminate the entire subscription unless the subscriber has lost + access to all resources in the topic array. +

+ +

+ Subscription-time authorization verifies that a subscriber has a legitimate + basis for receiving notifications. Delivery-time authorization ensures that + notifications remain consistent with the subscriber's current permissions, + accounting for authorization changes that may occur after the subscription + was created. +

+
+ +
+

Subscription Response

+ +

+ The response to a successful subscription creation request MUST conform to the + application/lws+json media type and MUST contain the following fields: +

+ + +
+
+ +
+

Webhook Notifications

+ +

+ Webhooks enable server-to-server notification delivery. After a webhook subscription is + created, the LWS storage server delivers notifications to the subscriber's + registered inbox when relevant changes occur. +

+ +
+

Creating a Webhook Subscription

+ +

+ When a client creates a webhook notification subscription, the subscription body MUST + include a type field equal to WebhookSubscription. In + addition, a server MUST support the following fields: +

+ + + +
+POST /subscriptions HTTP/2
+Host: notification.example
+Authorization: Bearer <access-token>
+Content-Type: application/lws+json
+
+{
+  "@context": ["https://www.w3.org/ns/lws/v1"],
+  "type": "WebhookSubscription",
+  "topic": [
+    "https://storage.example/alice/notes/",
+    "https://storage.example/alice/profile"
+  ],
+  "inbox": "https://receiver.example/hooks/lws",
+  "expires": "2026-06-09T12:00:00Z"
+}
+        
+ +

+ A successful response body has these additional requirements: +

+ + + +
+HTTP/2 200 OK
+Content-Type: application/lws+json
+Location: https://notification.example/subscriptions/9e8d7c6b5a4f
+
+{
+  "@context": ["https://www.w3.org/ns/lws/v1"],
+  "type": "WebhookSubscription",
+  "subscription": "https://notification.example/subscriptions/9e8d7c6b5a4f",
+  "expires": "2026-06-09T12:00:00Z"
+}
+        
+
+ +
+

Webhook Authentication

+ +

+ When a server delivers a notification to an inbox, the + subscriber needs to verify that the request is authentic. +

+ +

+ A server SHOULD sign each outbound request using HTTP Message Signatures + [[!RFC9421]]. When an inbox receives a signed message, it MUST + verify the signature using the notification server's public key, discoverable via + the storage description resource. +

+ +

+ A server that supports HTTP Message Signatures MUST include the signing key in the + storage description resource. The key MUST be expressed as a verification method + in a verificationMethod array, and MUST be referenced from an + authentication verification relationship, following the Controlled + Identifiers 1.0 [[!CID-1.0]] data model. +

+ +
+{
+  "@context": ["https://www.w3.org/ns/lws/v1"],
+  "id": "https://storage.example/",
+  "type": "Storage",
+  "verificationMethod": [{
+    "id": "https://storage.example/#key-20260320",
+    "type": "JsonWebKey",
+    "controller": "https://storage.example/",
+    "publicKeyJwk": {
+      "kid": "key-20260320",
+      "kty": "EC",
+      "crv": "P-256",
+      "alg": "ES256",
+      "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
+      "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
+    }
+  }],
+  "authentication": ["https://storage.example/#key-20260320"],
+  "service": [{
+    "type": "NotificationService",
+    "serviceEndpoint": "https://notification.example/subscriptions",
+    "subscriptionType": ["WebhookSubscription"]
+  }]
+}
+        
+ +
+

Signature Requirements

+ +

+ A server that signs webhook delivery requests MUST include at least the following + components in the signature base: +

+ +
    +
  • @method — the HTTP method of the request (always POST).
  • +
  • @scheme — prevents downgrade from https to http.
  • +
  • + @authority — the host of the inbox, + preventing delivery to an unintended endpoint. +
  • +
  • + @path — the path component of the inbox URL. +
  • +
  • + content-type — the media type of the request body. +
  • +
  • + content-digest — a digest of the request body, computed + according to [[!RFC9530]]. +
  • +
+ +

+ The Signature-Input field MUST include the created and + keyid parameters. The keyid value MUST correspond to + the id of a key in the verificationMethod array of the + storage description resource. +

+
+ +
+

Signature Verification

+ +

+ To verify a webhook signature, a receiver MUST perform the following steps: +

+ +
    +
  1. + Extract the keyid value from the Signature-Input + field. The keyid value MUST be a URL with a fragment component. +
  2. +
  3. + Remove the fragment component from the keyid URL. The resulting + URL is the storage identifier. +
  4. +
  5. + Dereference the storage identifier to retrieve the storage description resource. + The id property of the top-level document map MUST match the + storage identifier. +
  6. +
  7. + Find the object in the verificationMethod array whose + id property matches either the full keyid URL or the + fragment component of the keyid URL. +
  8. +
  9. + Verify the HTTP Message Signature using the located verification method. +
  10. +
+
+
+ +
+

Subscription Management

+ +

+ The value of the serviceEndpoint property for a + NotificationService that supports WebhookSubscription + MUST be a URL that supports GET operations to list a subscriber's + active webhook subscriptions. The resulting serialization MUST conform to the requirements + for LWS Containers [[!LWS-PROTOCOL]]. The response SHOULD support LWS Paging. +

+ +

+ Each subscription resource listed in this container MUST support GET + and DELETE operations. A GET request returns the current + state of the subscription. A DELETE request cancels the subscription. +

+
+ +
+

Notification Delivery

+ +

+ The server sends an HTTP POST request to the registered + inbox with the notification envelope as the request body. The + request body MUST conform to the application/lws+json media type. +

+ + +
+
+ +
+

Security Considerations

+ + +
+ +
+

Privacy Considerations

+ + +
+ +
+

Vocabulary

+ +

+ This document defines the following terms in the + https://www.w3.org/ns/lws# namespace: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TermURIDescription
Notificationlws:NotificationNotification envelope type
NotificationServicelws:NotificationServiceService type for notification discovery
WebhookSubscriptionlws:WebhookSubscriptionWebhook subscription type
subscriptionTypelws:subscriptionTypeSupported subscription types on a notification service
subscriptionlws:subscriptionURL of the subscription resource
activitylws:activityActivity Streams 2.0 payload within a notification
topiclws:topicArray of resource URIs included in a subscription
storagelws:storageURI identifying the LWS storage
+ +

+ This document uses the following terms from external vocabularies, which should be + added to the LWS JSON-LD context. The Activity Streams 2.0 terms are included in + the LWS context with their source IRIs from the + https://www.w3.org/ns/activitystreams# namespace. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TermURISource
Createhttps://www.w3.org/ns/activitystreams#CreateActivity Streams 2.0
Updatehttps://www.w3.org/ns/activitystreams#UpdateActivity Streams 2.0
Deletehttps://www.w3.org/ns/activitystreams#DeleteActivity Streams 2.0
objecthttps://www.w3.org/ns/activitystreams#objectActivity Streams 2.0
actorhttps://www.w3.org/ns/activitystreams#actorActivity Streams 2.0
targethttps://www.w3.org/ns/activitystreams#targetActivity Streams 2.0
originhttps://www.w3.org/ns/activitystreams#originActivity Streams 2.0
publishedhttps://www.w3.org/ns/activitystreams#publishedActivity Streams 2.0
verificationMethodhttps://w3id.org/security#verificationMethodControlled Identifiers 1.0
authenticationhttps://w3id.org/security#authenticationMethodControlled Identifiers 1.0
controllerhttps://w3id.org/security#controllerControlled Identifiers 1.0
publicKeyJwkhttps://w3id.org/security#publicKeyJwkControlled Identifiers 1.0
inboxhttp://www.w3.org/ns/ldp#inboxLinked Data Platform
expireshttps://schema.org/expiresSchema.org
+
+ + diff --git a/lws10-vocab/vocabulary.yml b/lws10-vocab/vocabulary.yml index d86e270..773c0b8 100644 --- a/lws10-vocab/vocabulary.yml +++ b/lws10-vocab/vocabulary.yml @@ -8,6 +8,10 @@ prefix: value: https://www.w3.org/ns/activitystreams# - id: schema value: https://schema.org/ + - id: sec + value: https://w3id.org/security# + - id: ldp + value: http://www.w3.org/ns/ldp# ontology: - property: dc:title @@ -41,6 +45,21 @@ class: comment: An OpenID Connect identity provider service. defined_by: https://www.w3.org/TR/lws-authn-openid/ + - id: Notification + label: Notification + comment: A notification envelope describing an event that occurred on a resource. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: NotificationService + label: Notification Service + comment: A service through which clients subscribe to resource change notifications. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: WebhookSubscription + label: Webhook Subscription + comment: A subscription type that delivers notifications via HTTP POST to a subscriber-provided inbox URL. + defined_by: https://www.w3.org/TR/lws-notifications/ + property: - id: items label: items @@ -93,6 +112,95 @@ property: label: Prefer Link Relations comment: A preference URI for including or omitting specific link relations in responses. + - id: subscriptionType + label: subscription type + domain: lws:NotificationService + comment: The subscription types supported by a notification service. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: subscription + label: subscription + range: xsd:anyURI + comment: The URL of the subscription resource or connection endpoint. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: activity + label: activity + domain: lws:Notification + comment: The Activity Streams 2.0 activity payload within a notification. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: topic + label: topic + range: xsd:anyURI + comment: An array of resource URIs covered by a subscription. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: storage + label: storage + range: xsd:anyURI + comment: The URI identifying the LWS storage that emitted the notification. + defined_by: https://www.w3.org/TR/lws-notifications/ + + - id: as:Create + label: Create + comment: An activity representing the creation of a resource. + + - id: as:Update + label: Update + comment: An activity representing the update of a resource. + + - id: as:Delete + label: Delete + comment: An activity representing the deletion of a resource. + + - id: as:object + label: object + comment: The primary object of an activity. + + - id: as:actor + label: actor + comment: The agent that performed the activity. + + - id: as:target + label: target + comment: The target of the activity. + + - id: as:origin + label: origin + comment: The origin of a Move activity. + + - id: as:published + label: published + range: xsd:dateTime + comment: The date and time the activity was published. + + - id: sec:verificationMethod + label: verification method + comment: A verification method associated with a key or identity. + + - id: sec:authentication + label: authentication + comment: An authentication method associated with an identity. + + - id: sec:controller + label: controller + comment: The entity that controls a verification method. + + - id: sec:publicKeyJwk + label: public key JWK + comment: A public key expressed as a JSON Web Key. + + - id: ldp:inbox + label: inbox + range: xsd:anyURI + comment: A URL to which notifications are delivered via HTTP POST. + + - id: schema:expires + label: expires + range: xsd:dateTime + comment: The date and time at which a subscription expires. + json_ld: alias: id: "@id"