Results of the Touchstone client sessions for the W3C Linked Web Storage (LWS) protocol, run against LWS client implementations: lws-client. A client session plays everything a client talks to (the storage, its authorization server, an OpenID Provider, the hosts of identity documents and the notification service that calls the client's inbox) and judges every request the client sends against the client requirements of the LWS specifications (the 5 October 2026 core Working Draft, the authentication suites' latest Working Drafts, and the editor's drafts of the webhook notification and index specifications). For this page Touchstone master 6a3206e took each client through every client rule, the checklist's tasks, and the faults a server may legally answer with (a refused linkset PUT, a lost create, an expired page of search results, an expired token, and four forged notifications). A client project may implement LWS in several languages; each language is tested as a client of its own, and grouped under its project. The server results are on LWS Conformance.

lws-client

Version main 99b8b10: eleven languages, each tested as a client of its own. Touchstone drove each client itself, through the driver at hellion.bmi.stonybrook.edu (an MCP server that runs one adapter per language over that language's library).

  • Java [ lws-client-java/0.1.0 ] — tested 2026-10-09 00:06:25 EDT (Touchstone client session XRJeM3WKsCR7jgiS): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • JavaScript [ lws-client-js/0.1.0 ] — tested 2026-10-09 00:06:38 EDT (Touchstone client session V-7l62Ilrilzwdug): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • C++ [ lws-client-cpp/0.1.0 ] — tested 2026-10-09 00:06:01 EDT (Touchstone client session c6lNx2k_aZ45CcuM): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • Rust [ lws-client-rust/0.1.0 ] — tested 2026-10-09 00:07:16 EDT (Touchstone client session ZLdFdOwrjGfstXRH): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • Go [ lws-client-go/0.1.0 ] — tested 2026-10-09 00:06:18 EDT (Touchstone client session -QZW5nk3dRyH_0O1): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • Python [ lws-client-python/0.1.0 ] — tested 2026-10-09 00:07:08 EDT (Touchstone client session W8cDA4wasWddZvGe): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • C# [ lws-client-csharp/0.1.0 ] — tested 2026-10-09 00:06:09 EDT (Touchstone client session OPHZCBub3BZOm154): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • Swift [ lws-client-swift/0.1.0 ] — tested 2026-10-09 00:07:24 EDT (Touchstone client session Zorw4HM10-uJvfNW): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • PHP [ lws-client-php/0.1.0 ] — tested 2026-10-09 00:06:58 EDT (Touchstone client session KaVUpW8r1U3wHBD9): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • Kotlin [ lws-client-kotlin/0.1.0 ] — tested 2026-10-09 00:06:46 EDT (Touchstone client session rSlwk3Pyk__AvxIh): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.
  • WASM [ lws-client-rust/0.1.0 (WebAssembly component) ] — tested 2026-10-09 00:07:32 EDT (Touchstone client session A7pkcU2mptOUDtHh): 53 rules, 50 passed, 0 failed, 3 untested, 0 inapplicable. No MUST rule failed: 38 of the 41 MUST rules that apply were exercised.

PASS / FAIL: at least one request was a trial of the rule, and none / at least one failed it (hover a cell for the number of trials). UNTESTED: nothing the client did was a trial of the rule, such as a rule about recovering from a refusal the client never provokes. INAPPLICABLE: the rule's area was left out of the session. Only MUST rules decide a verdict; a SHOULD or MAY failure is advisory. A rule name is coloured by its worst outcome across all the clients. Times are when each session's results began, in US Eastern time.

RequirementLevelRulelws-client
JavaJavaScriptC++RustGoPythonC#SwiftPHPKotlinWASM
Core
The client presents its access token to the storage in an Authorization Bearer header and nowhere elseMUSTclient-token-in-authorization-headerPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client replaces a linkset with PUT only after the linkset advertised PUTSHOULDclient-linkset-put-only-when-advertisedPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client patches a linkset only in a format the linkset advertisedSHOULDclient-linkset-patch-format-advertisedPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client makes a PUT that replaces a data resource conditionalSHOULDclient-put-conditionalPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client makes PUT and PATCH on a linkset conditionalSHOULDclient-linkset-write-conditionalPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client requests only the page URLs the server gave itSHOULDclient-page-urls-issuedPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client reaches resources through URLs the server gave it, not by building pathsSHOULDclient-member-urls-issuedPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's access requests and grants have an @context array that includes the LWS contextMUSTclient-access-document-contextPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's access requests have a type that includes AccessRequestMUSTclient-access-request-typePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's access grants have a type that includes AccessGrantMUSTclient-access-grant-typePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's access requests and grants name their storage with a URIMUSTclient-access-document-storagePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's access requests and grants carry an access array of one or more objectsMUSTclient-access-document-accessPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
Every policy in the client's access requests and grants has a type that includes AccessPolicyMUSTclient-access-policy-typePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
Every policy in the client's access requests and grants names one or more actions the server recognisesMUSTclient-access-policy-actionPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
Every policy in the client's access requests and grants names its assignee with a URIMUSTclient-access-policy-assigneePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
A policy target in the client's access requests and grants is an object with a type and one or more valuesMUSTclient-access-policy-targetPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
A policy's constraints in the client's access requests and grants are objects with leftOperand, operator and rightOperandMUSTclient-access-policy-constraintPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
An inbox in the client's access requests and grants is a URIMUSTclient-access-document-inboxPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client makes a DELETE conditional on the ETag it last readMAYclient-delete-conditionalPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The Link headers of the client's creates carry no server-managed relation, such as linksetMUSTclient-create-no-server-managed-linksPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
When the client creates a container, its POST carries Link rel="type" to lws#ContainerMUSTclient-create-container-type-linkPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
When the client deletes a container with everything in it, it sends Depth infinity, or deletes the members firstMUSTclient-delete-container-depthPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
When the client updates a resource's content and metadata in one request, it asks for that with Prefer set-linksetMUSTclient-combined-update-prefer-set-linksetUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTED
After a linkset refuses a request with 405 or 415, the client does not send the same request againMUSTclient-no-repeat-after-405-415PASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
After a create fails with a server error, the client checks before it POSTs the same thing againSHOULDclient-no-blind-retry-of-createPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
Authentication
The client's token exchange requests name the storage they want a token for in resourceMUSTclient-token-exchange-resourcePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's token exchange requests present an authentication credential in subject_tokenMUSTclient-token-exchange-subject-tokenPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
After a 401, the client asks for a token only for a realm that contains the URL it was refusedMUSTclient-token-for-containing-realmPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client presents a self-issued credential with the jwt token typeMUSTclient-cid-token-type-jwtPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential is signed, not alg noneMUSTclient-cid-credential-signedPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential names its subject, a URI, in subMUSTclient-cid-subject-claimPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential names its issuer, a URI, in issMUSTclient-cid-issuer-claimPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential names the client in client_idMUSTclient-cid-client-id-claimPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential has the same URI in sub, iss and client_idMUSTclient-cid-identifiers-agreePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The audience of the client's self-issued credential includes the authorization serverMUSTclient-cid-audience-includes-asPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential restricts its audience with audSHOULDclient-cid-audience-restrictedPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential says when it expires, in expMUSTclient-cid-expiry-claimPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's self-issued credential says when it was issued, in iatMUSTclient-cid-issued-at-claimPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client presents an ID Token with the id_token token typeMUSTclient-oidc-token-type-id-tokenPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client presents a SAML 2.0 assertion with the saml2 token typeMUSTclient-saml-token-type-saml2UNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTED
Notifications
The client sends a subscription request as an application/lws+json objectMUSTclient-subscription-media-typePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's subscription requests have a type the NotificationService advertisesMUSTclient-subscription-typePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's subscription requests have a topic array of URIsMUSTclient-subscription-topicPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's webhook subscription requests name an inbox URIMUSTclient-subscription-inboxPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
Each of the client's webhook subscriptions has an inbox URL of its ownMAYclient-subscription-own-inboxPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's inbox acknowledges a genuine notification with a 2xxSHOULDclient-inbox-acknowledges-genuine-deliveryPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's inbox refuses a notification signed with a key the storage description does not publishMUSTclient-inbox-refuses-unpublished-keyPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's inbox refuses a notification whose body no longer matches its signed Content-DigestMUSTclient-inbox-refuses-altered-bodyPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's inbox refuses a notification whose keyid is not a URL with a fragmentMUSTclient-inbox-refuses-keyid-without-fragmentPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
The client's inbox refuses a notification whose keyid names a document that is not the storage description it claims to beMUSTclient-inbox-refuses-foreign-key-documentPASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
Index
The client's search requests carry a Content-Type naming their query formatMUSTclient-query-content-typePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS
After a search format is refused with 415, the client's next search uses the baseline formatMUSTclient-query-baseline-after-415UNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTEDUNTESTED
After a page of search results is refused, the client restarts the searchSHOULDclient-restart-after-refused-pagePASSPASSPASSPASSPASSPASSPASSPASSPASSPASSPASS