This document extends the Linked Web Storage (LWS) protocol with a mechanism for clients to receive timely updates about changes to resources. Three notification channels are defined: Server-Sent Events, WebSocket, and Webhooks. All channels share a common notification data model and are 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 through one of three channels:
All three channels share a common notification envelope and are discoverable via the storage description resource.
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",
"WebSocketSubscription",
"EventSourceSubscription"
]
}]
}
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.
phase — OPTIONAL. A string indicating at which point in
the resource lifecycle the notification was emitted. This specification defines
the value PostCommit, which indicates that the described change
has already been persisted.
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",
"phase": "PostCommit",
"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",
"phase": "PostCommit",
"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 the following fields:
phase — OPTIONAL. A string indicating the lifecycle phase for
which notifications are requested. This specification defines the value
PostCommit.
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": "EventSourceSubscription",
"phase": "PostCommit",
"topic": [
"https://storage.example/alice/notes/",
"https://storage.example/alice/profile"
]
}
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.
The WebSocket notification channel allows a subscriber to receive notifications over a persistent WebSocket connection.
When a client creates a WebSocket notification subscription, the subscription body
MUST include a type field equal to WebSocketSubscription.
A successful response body has these additional requirements:
type — the value of this property MUST be a string equal to
WebSocketSubscription.
subscription — the value of this property MUST be a
capability URL that can be used by the WebSocket API.
{
"@context": ["https://www.w3.org/ns/lws/v1"],
"type": "WebSocketSubscription",
"subscription": "wss://notification.example/ws/1c2d3e4f5a6b"
}
After obtaining a capability URL, the subscriber opens a WebSocket connection to that URL. The server sends notification envelopes as WebSocket text messages, serialized as JSON.
const ws = new WebSocket(capabilityUrl);
ws.onmessage = (event) => {
const notification = JSON.parse(event.data);
// handle notification
};
The lifecycle of a WebSocket subscription is tied to the connection. When the WebSocket connection is closed, the subscription is terminated.
The Server-Sent Event (SSE) notification channel allows a subscriber to receive notifications over a persistent HTTP connection using the EventSource API.
When a client creates an EventSource notification subscription, the subscription body
MUST include a type field equal to EventSourceSubscription.
A successful response body has these additional requirements:
type — the value of this property MUST be a string equal to
EventSourceSubscription.
subscription — the value of this property MUST be a
capability URL that can be used by the EventSource API.
{
"@context": ["https://www.w3.org/ns/lws/v1"],
"type": "EventSourceSubscription",
"subscription": "https://notification.example/sse/0d8b4c2e1a3f"
}
After obtaining a capability URL, the subscriber establishes a
persistent SSE connection to that URL. The server responds with Content-Type:
text/event-stream and pushes notification envelopes as events. Each event
MUST include an id field to support reconnection via the
Last-Event-ID header.
const source = new EventSource(capabilityUrl);
source.onmessage = (event) => {
const notification = JSON.parse(event.data);
// handle notification
};
The lifecycle of an EventSource subscription is tied to the connection. When the connection is closed, the subscription is terminated.
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",
"phase": "PostCommit",
"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 |
WebSocketSubscription |
lws:WebSocketSubscription |
WebSocket subscription type |
EventSourceSubscription |
lws:EventSourceSubscription |
Server-Sent Event subscription type |
WebhookSubscription |
lws:WebhookSubscription |
Webhook subscription type |
PostCommit |
lws:PostCommit |
Notification phase indicating an event occurred after resource persistence |
subscriptionType |
lws:subscriptionType |
Supported subscription types on a notification service |
subscription |
lws:subscription |
URL of the subscription resource |
phase |
lws:phase |
Lifecycle phase of the notification |
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 |