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 @@
+ 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. +
++ 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. +
++ 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: +
+ +POST. An inbox is
+ used whenever the recipient of a notification is a server rather than a client
+ holding an open connection.
+
+ 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.
+
+ 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.
+
+ A notification envelope has the following properties: +
+ +type — REQUIRED. The value MUST be the string
+ Notification.
+ storage — REQUIRED. A URI identifying the LWS storage with
+ which the notification is associated.
+ activity — REQUIRED. A JSON object or an array of JSON objects,
+ each conforming to the Activity Streams 2.0 data model, describing the event or
+ events that occurred. See .
+
+{
+ "@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"
+ }
+}
+
+
+ Each activity object within the activity property MUST conform to the
+ Activity Streams 2.0 data model and MUST include the following properties:
+
id — REQUIRED. A string uniquely identifying the activity.
+ type — REQUIRED. An array of strings containing at least one
+ Activity Streams 2.0 activity type.
+ object — REQUIRED. A JSON object identifying the resource
+ that the notification is about. The object MUST include the
+ following properties:
+ id — REQUIRED. The URI of the resource.
+ type — REQUIRED. An array of strings containing at least
+ one resource type. Values defined by [[!LWS-PROTOCOL]] include
+ Container and DataResource. Additional type values
+ MAY be included.
+ published — REQUIRED. A datetime value conforming to
+ [[!RFC3339]] indicating when the activity occurred.
+ + The following properties are OPTIONAL: +
+ +actor — a URI identifying the agent that performed the action.
+ target — a URI identifying the container into which the
+ resource was added. Used with Create activities.
+ origin — a URI identifying the container from which the
+ resource was removed. Used with Delete activities.
+ + A server MUST support the following Activity Streams 2.0 activity types to + indicate LWS resource changes: +
+ +Create — a new resource was created in a container. The
+ target field indicates the container to which the resource was added.
+ Update — an existing resource's content or metadata was modified.
+ Delete — a resource was removed. The origin field
+ indicates the container from which the resource was removed.
+ + Other activity types MAY also be supported. +
+
+ 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"
+ }
+ ]
+}
+
+
+ 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.
+
+ A subscription request MUST contain the following fields: +
+ +type — REQUIRED. A string identifying the subscription type.
+ The value MUST be one of the types listed in the subscriptionType
+ array of the NotificationService.
+ topic — REQUIRED. An array of URIs identifying the resources
+ included in the subscription.
+ + 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"
+}
+
+ + 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. +
+
+ The response to a successful subscription creation request MUST conform to the
+ application/lws+json media type and MUST contain the following fields:
+
type — REQUIRED. A string equal to the subscription type from
+ the request.
+ subscription — REQUIRED. A URL representing the subscription.
+ + 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. +
+ +
+ 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:
+
inbox — REQUIRED. The URI of the inbox to which
+ notification messages are delivered.
+ expires — OPTIONAL. A datetime value indicating when the
+ subscription expires.
+
+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: +
+ +type — the value of this property MUST be a string equal to
+ WebhookSubscription.
+ subscription — a URL that can be used to manage the lifecycle of
+ the subscription.
+ expires — OPTIONAL. A datetime value indicating when the
+ subscription expires.
+
+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"
+}
+
+ + 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"]
+ }]
+}
+
+
+ + 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.
+
+ To verify a webhook signature, a receiver MUST perform the following steps: +
+ +keyid value from the Signature-Input
+ field. The keyid value MUST be a URL with a fragment component.
+ keyid URL. The resulting
+ URL is the storage identifier.
+ id property of the top-level document map MUST match the
+ storage identifier.
+ verificationMethod array whose
+ id property matches either the full keyid URL or the
+ fragment component of the keyid URL.
+
+ 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.
+
+ 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.
+
created parameter in the Signature-Input field and reject
+ signatures whose timestamp falls outside a reasonable clock-skew window.
+ actor property in
+ notifications reveals who made changes. This may be inappropriate in some contexts.
+ The actor property SHOULD be omitted by default. Servers MAY make
+ its inclusion configurable.
+
+ This document defines the following terms in the
+ https://www.w3.org/ns/lws# namespace:
+
| Term | +URI | +Description | +
|---|---|---|
Notification |
+ lws:Notification |
+ Notification envelope type | +
NotificationService |
+ lws:NotificationService |
+ Service type for notification discovery | +
WebhookSubscription |
+ lws:WebhookSubscription |
+ Webhook subscription type | +
subscriptionType |
+ lws:subscriptionType |
+ Supported subscription types on a notification service | +
subscription |
+ lws:subscription |
+ URL of the subscription resource | +
activity |
+ lws:activity |
+ Activity Streams 2.0 payload within a notification | +
topic |
+ lws:topic |
+ Array of resource URIs included in a subscription | +
storage |
+ lws:storage |
+ URI 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.
+
| Term | +URI | +Source | +
|---|---|---|
Create |
+ https://www.w3.org/ns/activitystreams#Create |
+ Activity Streams 2.0 | +
Update |
+ https://www.w3.org/ns/activitystreams#Update |
+ Activity Streams 2.0 | +
Delete |
+ https://www.w3.org/ns/activitystreams#Delete |
+ Activity Streams 2.0 | +
object |
+ https://www.w3.org/ns/activitystreams#object |
+ Activity Streams 2.0 | +
actor |
+ https://www.w3.org/ns/activitystreams#actor |
+ Activity Streams 2.0 | +
target |
+ https://www.w3.org/ns/activitystreams#target |
+ Activity Streams 2.0 | +
origin |
+ https://www.w3.org/ns/activitystreams#origin |
+ Activity Streams 2.0 | +
published |
+ https://www.w3.org/ns/activitystreams#published |
+ Activity Streams 2.0 | +
verificationMethod |
+ https://w3id.org/security#verificationMethod |
+ Controlled Identifiers 1.0 | +
authentication |
+ https://w3id.org/security#authenticationMethod |
+ Controlled Identifiers 1.0 | +
controller |
+ https://w3id.org/security#controller |
+ Controlled Identifiers 1.0 | +
publicKeyJwk |
+ https://w3id.org/security#publicKeyJwk |
+ Controlled Identifiers 1.0 | +
inbox |
+ http://www.w3.org/ns/ldp#inbox |
+ Linked Data Platform | +
expires |
+ https://schema.org/expires |
+ Schema.org | +