Skip to content

feat(solid): work on Linked Web Storage servers - #604

Draft
ixuz wants to merge 9 commits into
mainfrom
css-lws
Draft

ixuz wants to merge 9 commits into
mainfrom
css-lws

Conversation

@ixuz

@ixuz ixuz commented Nov 6, 2026

Copy link
Copy Markdown
Member

What and why

Draft, to show the work and try CI. It will be split before merging.

#594 asks applications to test against servers that implement Linked Web Storage (LWS). This runs Solid Memo against the Community Solid Server fork in jeswr/CommunitySolidServer#126, and makes the app work on the fork's LWS-only preset.

  • Advisory servers: css-lws (Solid and LWS side by side) and css-lws-only, built from a pinned fork commit. A person approves each newer commit on the Dependency Dashboard.
  • Finding the storage: through Link rel=lws#storage and the storage description. HEAD requests that read links now ask for Turtle first.
  • Sign-in: solid-memo-client exchanges the OIDC ID token for a Bearer token per storage (RFC 8693). A build that signs in to an LWS storage needs SOLID_CLIENT=solid-memo-client.
  • Bridge: after an LWS server refuses a Turtle listing, a PUT that creates a document, or a SPARQL PATCH, the bridge makes the request the LWS way. A Solid server gets each request once, as before.
  • Docs: docs/lws.md explains the above and what LWS can't give the app yet.

Test results

  • The pod tests pass on both servers: 106 passed, 3 skipped.
  • Of the 28 Chromium journeys on LWS-only, 20 pass and 8 fail. Those 8 (pod-safety, second-instance, tour-errors) use a test helper whose Solid-OIDC token the storage refuses, so what the app does in them is still untested there.
  • CI runs no authenticated LWS test. Sign-in is covered by unit tests and by journeys run by hand.

Suggested split: (a) the css-lws server and Renovate, (b) discovery, (c) the bridge, (d) LWS sign-in, (e) the harness and css-lws-only, (f) docs.

Refs: #594

ixuz added 7 commits November 6, 2026 12:48
The Linked Web Storage protocol is being written now, and the Solid CG
and the LWS WG asked applications to test against servers that
implement it. The Community Solid Server's LWS work isn't released: it
lives on a branch of jeswr's fork (pull request 126 there), on the same
8.0.0-alpha.3 as css-8. css-lws builds that branch at one commit and
runs it as an advisory server, so its results are reported, not a gate.

Its configuration is the fork's solid-lws.json, the Solid Protocol and
LWS side by side, in memory and with the root as the storage, as the
tests need. Every test passes there with both Solid clients, as on
css-8. The fork's LWS-only configuration isn't in the matrix: it serves
a Solid-OIDC request as anonymous, so the app can't sign in there, and
the tests send no credentials, so they can't show it.

Refs: #594
css-lws builds a commit of a fork nobody has reviewed, on CI's runners
and on contributors' machines. Renovate follows the fork's branch, but a
person approves each newer commit on the Dependency Dashboard before it
is proposed, and merges it after reading the change. Its image keeps to
Node 22, which the fork's own Dockerfile runs.

Refs: #594
A server that speaks Linked Web Storage (LWS) alone names a resource's
storage with a Link of relation lws#storage on every answer, and the
storage's description (application/lws+cid) names its root container.
It doesn't type the storage pim:Storage, and the Community Solid
Server with LWS answers its storage's URI, its root container's too,
with that description when asked for anything, which has none of the
Solid links. So the app found no storage there, couldn't make a type
index, and couldn't make a release public: the ACL lookup never
reached the root's ACL.

The storage walk-up takes an LWS storage's roots from its description
when the WebID's document is in one, and a deck's folder is a
storage's root only when the description names it. Each HEAD that
reads a resource's links asks for Turtle first, or anything else, so
the root keeps its Solid links where the server has Turtle, and a
server that lists folders only as LWS has them still answers.

Refs: #594
…akes them

LWS Protocol 1.0 doesn't ask a server to take three requests the app
makes of a Solid pod: a folder's listing in Turtle, a document made
with PUT at a name the app chooses, and an edit with SPARQL Update.
A server that takes only LWS refuses them, and the app can't work.

A bridge below the fences sends each request as the app makes it, and
only when an LWS server refuses one makes it as LWS has it: the
listing read as application/lws+json, page by page; the document
POSTed to its folder by the name asked for, each missing folder above
it first, and refused where the server names it otherwise, since an
instance's addresses are the app's to choose; the edit applied here
and written whole with If-Match, tried again where another write came
first. A Solid server gets each request once, as before.

Refs: #594
An LWS storage takes no DPoP-bound Solid-OIDC token: it takes a Bearer
token its authorization server issues for the OpenID Connect ID token,
in an OAuth 2.0 Token Exchange. The Community Solid Server with LWS
alone serves a DPoP request as no one's, so the app could read only
what is public there, and write nothing.

The client keeps the ID token in memory, and on a storage's LWS
challenge checks what it names as LWS asks a client to, exchanges the
ID token for that storage's token, and sends the request again with
it. The ID token goes only to an authorization server on the identity
provider's origin, as LWS binds it to none, and each storage's token
only into that storage. The fork's challenge names its authorization
server with as_uri, as an earlier draft had it; that is taken too.
Inrupt's sign-in client gives the app no ID token, so this is the own
client's alone.

Refs: #594
css-lws answers the app's Solid requests, so it shows nothing of what
the app does where LWS alone is spoken. css-lws-only runs the same
image with the fork's LWS-only configuration, as an advisory server.

The tests' requests go through the bridge to an LWS server, as the
app's do, and the contract's for a server SOLID_SERVER_URL names; its
probe document is made only where none is, as the app makes one. The
fork takes Solid's requests even with LWS alone, so a proxy that
refuses them (npm run lws:strict) shows the bridge's own way of making
them, for a contributor's runs, with E2E_SEEDS_AS_LWS=1 making a
test's own seeds as LWS does. A compose file runs the fork's own
LWS-only preset behind the journeys' TLS, for the journeys by hand.

Refs: #594
A contributor meeting the bridge, the storage description or the
token exchange needs to know why each is made the way it is, and what
LWS can't give the app yet, such as a release anyone can read. The
testing page names css-lws-only and how the bridge's own way of making
requests is tried, and the standards page names LWS and the OAuth
specifications it uses.

Refs: #594
@ixuz ixuz added enhancement New feature or request interop labels Nov 6, 2026
Comment thread e2e/pod/strictLws.ts Fixed
Comment thread e2e/pod/strictLws.ts Fixed
Comment thread packages/solid/src/lwsBridge.ts Fixed
ixuz added 2 commits November 6, 2026 13:22
A request line may name a host of its own, as an absolute target or
as //host/, and new URL(request.url, server) then went to that host,
so anything that could reach the port could send requests elsewhere
through the proxy, and read their answers. CodeQL flags it as
server-side request forgery. Only the request's path and query are
taken now, onto the server's own URL.

Refs: #594
The listing looked for the profile's URI anywhere in Content-Type, so
a longer URI, or the URI in another parameter, also made the bridge ask
the server whether it takes LWS. CodeQL flags the substring check. The
profile parameter's list is read now and each entry compared whole.

Refs: #594

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request interop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants