Use when a Solid app works on one server and breaks on another — auth succeeds on PodSpaces but CSS rejects 'iat is not recent enough', 403 on PATCH after writing an ACL, missing type indexes, 500 on PUT, notifications that won't subscribe, or an apparent CORS error. Differential reference for CSS / ESS / NSS with runtime detection.
Install
npx skillscat add jeswr/solid-sdk/apps-issues-claude-skills-solid-server-matrix Install via the SkillsCat registry.
Solid server matrix
Solid is a protocol with several independent server implementations, and they
diverge in ways the spec permits or doesn't yet pin down. An app written and
tested against one server silently depends on that server's behaviour. This
skill is the differential model: identify which server you're on, then branch.
It assumes the house stack from `AGENTS.md`:@solid/reactive-authentication (auth, DPoP), @jeswr/fetch-rdf (fetch+parse),@solid/object + @rdfjs/wrapper (typed RDF), n3. Never @inrupt/*,@ldo/*, or hand-built triples. RDF mutations go through TermWrapper
subclasses. This skill diagnoses server behaviour; it does not change the
client libraries.
Local two-instance setup (one WAC, one ACP CSS) is in AGENTS.md §"Servers —
develop, test, release". Exercise both from day one.
The implementations
| Short | Full name | Maintainer | Status |
|---|---|---|---|
| CSS | Community Solid Server | Ruben Verborgh / university lab | Active, spec-tracking; house default for local dev |
| ESS | Enterprise Solid Server | Inrupt | Active, commercial; backs Inrupt PodSpaces |
| NSS | node-solid-server | Solid community | Legacy — preceded CSS, non-conformant in places; avoid for new work |
Authorship + "ACL implemented for NSS, ACP on ESS" confirmed:
forum/bridging-solid-with-peergos/5494.
"NSS isn't an Inrupt product, it is a legacy community project that preceded CSS":
forum/usage-of-as-uri/6462.
The canonical list of what is deployable and deployed is
solidproject.org/users/get-a-pod. It names six
self-hostable implementations — CSS, ESS,
Trinpod,
Manas, NSS, and
PHP Solid Server — of which this matrix
characterises the three you will meet at a hackathon (CSS / ESS / NSS); treat the other three
as "detect at runtime" (next section).
Hosted providers (get-a-pod, June 2026 × solid/catalog catalog-data.ttl)
| Provider | Operator | Region | Implementation (per catalog) |
|---|---|---|---|
solidcommunity.net |
Solid Project | UK | Pivot (CSS remix) + SolidOS |
Inrupt Pod Spaces (storage.inrupt.com) |
Inrupt | US, EU, APAC | ESS |
*.inrupt.net |
Inrupt | — | NSS (legacy) — community knowledge, not in catalog |
Data Pod (datapod.igrant.io) |
iGrant.io | EU | NSS |
redpencil.io |
redpencil.io | EU | CSS |
solidcommunity.au |
Solid Community AU | Australia | not in catalog |
solidweb.me |
Meisdata | EU | CSS + SolidOS |
teamid.live |
Meisdata | EU | Pivot + SolidOS (catalog status: Exploration) |
solidweb.app |
Meisdata | EU | not in catalog |
solidweb.org |
Solid Grassroots | EU | NSS + SolidOS (catalog status: Exploration) |
trinpod.eu / trinpod.us |
Graphmetrix | EU / US | Trinpod |
use.id |
Digita | EU | CSS |
localhost:3000 (default config) |
you | — | local CSS (this repo's AGENTS.md) |
Most hosted pods run CSS or its Pivot remix — but two get-a-pod providers run legacy NSS
(Data Pod, solidweb.org), so the NSS column of the matrix is live, not historical. Use the
table as the first guess, then confirm at runtime (next section); never branch on the
hostname alone. The final-stage test matrix in AGENTS.md §Servers is exactly this provider
list.
Compatibility matrix
Branch on these. Each cell carries evidence or is marked [unverified].
| Dimension | CSS | ESS | NSS (legacy) |
|---|---|---|---|
| Auth | Solid-OIDC + DPoP, strict | Solid-OIDC + DPoP | Older WebID-OIDC; non-conformant discovery [u] |
DPoP iat unit |
Rejects ms — needs seconds¹ | Tolerates anything (ms, 0, future) ¹ | unverified |
UMA as_uri in WWW-Authenticate |
Implemented (per thread) ² | Yes — UMA-compliant ² | No ² |
| Access control | WAC (.acl) default ³ |
ACP (.acr) ³ |
WAC (.acl) ³ |
| Type-index auto-seed | No — app must create + link ⁴ | unverified (commonly seeded) [u] | NSS historically seeds [u] |
| Content-Type required on write | strict per protocol ⁵ | strict | 5.x: 500 without it ⁵ |
| ETag reliability | available | available [u] | Not supported ⁶ |
| Notifications | Notifications Protocol / WebSocketChannel2023 ⁷ |
@inrupt/solid-client-notifications (ESS-only) ⁸ |
legacy wss:// channel ⁸ |
| Large-container listing | slow at scale (LDP) [u] | slow at scale [u] | very slow; glob OOMs; no SPARQL ⁹ |
[u] = unverified in this trawl. Footnotes:
- DPoP proof
iatmust be seconds since epoch (Math.floor(Date.now()/1000)),
not ms. ESS "doesn't appear to check the IAT at all"; CSS rejects with400 invalid_dpop_proof / "iat is not recent enough".
forum/.../dpop-rejects-iat-on-community-solid-server/7444.
The house auth lib (@solid/reactive-authentication) gets this right — if you
hit it, suspect a second auth layer you added (AGENTS.md §Authentication). - ESS "returns a UMA-compliant
WWW-Authenticateheader with anas_uri
parameter on unauthenticated requests"; NSS does not; CSS has since implemented
it. forum/usage-of-as-uri/6462. - WAC on NSS+CSS, ACP on ESS:
forum/bridging-solid-with-peergos/5494,
forum/solid-servers-and-custom-ontologies/7208.
Deployment count (March 2026 community survey): WAC 13 implementations / 11 live; ACP 4 / 1. - CSS does not provision type-index / preferences / inbox: "things like
typeIndexes and preferences and even the inbox are not available." TBL, asked
whether apps should create them: "Yes" … "The same place they are now."
forum/data-discovery-on-community-solid-server/4695.
Convention, not server-enforced: "a type index is not enforced by the server, it
requires each app to enforce the type index."
forum/solid-interop-in-practice/7701. - "Version 5.x NSS currently gives a 500 error for attempts to create a resource
without a content-type … it is actually invalid to try to create a resource
without a specified content-type."
forum/handling-duplicate-file-names/2016.
Also: forceContent-Typeon Turtle writes or it is parsed as N3:
forum/create-a-acl-with-solid-file-client/1538. - "ETags are not supported in node-solid-server."
forum/state-of-the-art-for-querying-large-containers/3320.
Implication for the conditional-PUT write path: NSS may give you no ETag, soIf-Matchis unavailable — handle anulletag (AGENTS.md §Writing data). - WebSocketChannel2023 type IRI
http://www.w3.org/ns/solid/notification#WebSocketChannel2023:
spec. Discovery
viaLink rel="describedby"(resource) /rel="…#storageDescription"(storage):
Solid Notifications Protocol. - "currently
@inrupt/solid-client-notificationsonly works with ESS"; NSS uses an
older websocket "and I don't know if it will be ported." Do not adopt that
Inrupt lib (banned stack); use plain WebSocket + discovery.
forum/does-solid-client-notifications-only-works-with-ess-pods/5401. - 1411 documents: container GET "takes more than a second", loading all ~3 min;
glob "500 error (probably … out-of-memory)"; "SPARQL is still not supported".
forum/state-of-the-art-for-querying-large-containers/3320.
That thread's OP worked around it by local caching, not sharding — sharding is
a design recommendation, not a quoted fix.
Runtime detection — what am I talking to?
Don't trust the hostname. Probe the live server.
1. Access-control language (WAC vs ACP). GET (or HEAD) the resource and read
its Link: <…>; rel="acl" header — never guess the URL. Then fetch that document
and inspect it: WAC documents use acl: (AclResource/Authorization); ACP
documents use acp: (AccessControlResource/Policy/Matcher). The local ACP
CSS instance in AGENTS.md advertises Link: <…/.acr>; rel="acl". Parse only
through @solid/object's typed classes; convert with wacToAcp / acpToWac —
never hand-parse the Turtle.
2. Auth model (Solid-OIDC + UMA). Hit a protected resource unauthenticated and
read WWW-Authenticate. An as_uri= parameter → UMA flow (ESS). Absent → plain
Solid-OIDC. Parse the header with the content-type-style approach, not regex
guessing.
3. Notifications. GET the resource and the storage root; look forLink rel="describedby" (resource-level subscription service) andrel="…/solid/terms#storageDescription" (storage-level). Fetch the description,
read the offered notify:channelType; branch CSS-modern (WebSocketChannel2023)
vs NSS-legacy (wss://). Discovery, not hardcoded endpoints.
4. ETag presence. First write returns the ETag (or doesn't, on NSS). If null,
skip If-Match and accept last-write-wins, or refuse to write — your call, but
detect it; don't assume an ETag exists.
"Where does this app's data live?" — decision tree
For a hackathon or first integration:
- Local CSS, in-memory (AGENTS.md). Two instances, WAC + ACP. Disposable,
pristine on restart, both auth languages from day one. Default for build/iterate. - solidcommunity.net (hosted CSS) — real-network smoke test, WAC. Community
service, no SLA. On the auth issuer list. - Inrupt PodSpaces (ESS) — the only place you exercise ACP + UMA
as_uri
in production. On the auth issuer list. Catches WAC-only code that breaks on ESS. - NSS / inrupt.net — only if you must interop with a legacy pod. Expect
non-conformant OIDC discovery, no ETags, 500-without-Content-Type. New work: avoid.
Rule: if it works on CSS and ESS, it works almost everywhere; testing one alone
hides the WAC↔ACP and DPoP-strictness divergences above.
Common misdiagnoses
- "CORS error." Most browser-reported CORS failures on Solid are really a
401 surfaced as CORS, or a reverse proxy in front of self-hosted CSS
strippingAccess-Control-Allow-*/-Expose-Headers(notably the auth andLocationheaders). Solid mandates permissive CORS by design — security rests
on WAC/OIDC, not origin. Check the actual status and the proxy before chasing CORS
config. The canonical working proxy snippet was not found in the forum trawl
(it lives in CSS GitHub Discussions) — unverified here.
(forum/cors-error-in-solidserver/6549,
forum/noobie-question-isnt-it-really-bad-to-mandate-permissive-cors/3439) - "Auth works on PodSpaces, breaks on CSS with
iat is not recent enough." A
DPoP proof carryingiatin ms. ESS tolerates it, CSS rejects it. See footnote 1. 403on PATCH right after writing an ACL. URI-unsafe characters — notably a
colon:— in the resource name break ACL matching (encoded-vs-unencoded
mismatch). Rename to URI-safe characters.
forum/error-403-for-patch-after-creating-document-acl/3426.- Type-index reads return nothing / writes have nowhere to go. CSS didn't seed
the index; your app must create-and-link it. Footnote 4. 500on PUT. NSS 5.x with noContent-Type. Always send it. Footnote 5.- Session dropped after navigation/reload. A hard reload drops in-memory tokens;
full-page reloads destroy the auth context. Use client-side navigation; the house
auth lib re-runsprompt=nonesilently while the IdP cookie lives (AGENTS.md
§Authentication).
forum/losing-session-when-going-to-different-page-after-login/5077. - Container request 301-redirects / relative IRIs resolve wrong. Missing trailing
slash. Container URLs end in/; the IdP/login URL is not the container URL.
forum/basic-question-about-url-to-use-when-making-a-request/4605.