| name | solid-lws-server | ||||
|---|---|---|---|---|---|
| description | Run and use the experimental native `sparq-lws-core` Solid/LDP (Linked Web Storage) server: configure its environment and storage backend, make authenticated LDP and WAC requests, negotiate Turtle or profile-aware JSON-LD, and query the WAC-scoped `/sparql` endpoint. Use for the Rust LWS server, not the separate `@sparq-org/solid-server` JavaScript development host. | ||||
| license | MIT | ||||
| metadata |
|
Use sparq-lws-core as an experimental native Solid/LDP server. It is not a
replacement for the supported TypeScript prod-solid-server, and its default
storage is ephemeral. Use skills/javascript-wasm/SKILL.md instead for the
separate @sparq-org/solid-server loopback development host.
Run the default build with the in-memory backend:
cargo run -p sparq-lws-coreIt listens on 127.0.0.1:3000 and uses http://localhost:3000 as its public
base URL unless configured otherwise. The default Cargo features are:
embedded-sparq, which enables the in-process SPARQ engine backend.sparql-endpoint, which mounts the query-only/sparqlroute.
Use --no-default-features for the engine-free Solid core tier; that tier keeps
LDP and WAC but does not expose /sparql.
For a deployed instance, set at least:
SOLID_SERVER_BASE_URL=https://solid.example \
SOLID_SERVER_BIND=127.0.0.1:3000 \
SOLID_SERVER_TRUSTED_ISSUER=https://idp.example \
cargo run -p sparq-lws-coreSOLID_SERVER_BASE_URL must be the public origin seen by clients and encoded in
resource IRIs. SOLID_SERVER_AUDIENCE defaults to that URL. Keep
SOLID_SERVER_ALLOW_LOOPBACK and SOLID_SERVER_BIDIRECTIONAL=off for local
development or conformance work only; the normal posture requires HTTPS IdP and
WebID URLs, DPoP, and strict WebID-to-issuer verification.
Terminate TLS at a trusted reverse proxy, or set both
SOLID_SERVER_TLS_CERT and SOLID_SERVER_TLS_KEY to readable PEM paths.
Setting only one makes startup fail. See skills/http3-server/SKILL.md for the
default-off http3 feature and skills/helm-deploy/SKILL.md for Kubernetes.
Set PSS_SPARQ_BACKEND to one of:
memory(default): an ephemeral in-memory test double.embedded: the default-enabled in-process engine. SetSOLID_SERVER_SPARQ_DIRfor a directory-backed graph; without it the graph is ephemeral.http: a remote SPARQ service. Build with--features http-sparqand setSOLID_SERVER_SPARQ_ENDPOINTto that service's/sparqlURL.
The native binary currently uses an in-memory blob backend. A durable/shared RDF index therefore does not by itself make resource bodies durable; do not present the native image as a durable production data service.
The seed variables are test-only:
SOLID_SERVER_SEED_CONFORMANCE=1provisions conformance fixtures.SOLID_SERVER_SEED_DEMO=1provisions a shared public demo playground.SOLID_SERVER_SEED_BENCH=1provisions benchmark fixtures.
They are refused on non-memory backends unless
SOLID_SERVER_ALLOW_SEED_NONMEMORY=1 is explicitly set. Use that escape hatch
only for an ephemeral test instance.
Except for public reads and discovery/health routes, requests pass through Solid-OIDC access-token verification and DPoP proof verification, then WAC. Supply both headers:
Authorization: DPoP ACCESS_TOKEN
DPoP: FRESH_REQUEST_BOUND_PROOF
Generate the DPoP proof for the exact HTTP method and target URI. Reusing a
proof fails because its jti is replay-protected. A valid token proves identity
but does not bypass WAC; the applicable resource or inherited container ACL
must grant the requested acl:Read, acl:Write, acl:Append, or
acl:Control mode.
A rule may name its subject with acl:agent, acl:agentClass foaf:Agent (public)
or acl:AuthenticatedAgent, or acl:agentGroup. A group grant resolves only when
the group document — the group IRI without its fragment — is stored on this pod
and lists the requester as <group> vcard:hasMember <webid>. Everything else is
fail-closed and grants nothing: an anonymous request, a group document that is
missing, malformed or silent about the requester, and a group IRI on another origin
(the server resolves membership from its own storage and makes no outbound fetch).
A group grant is per-requester, so it never appears in the public= audience of
WAC-Allow.
SOLID_SERVER_IDENTITY_ENABLE=1 is off by default. When set, the server serves
provider-issued WebID documents from a separate identity host — id.<base authority>
unless SOLID_SERVER_IDENTITY_HOST overrides it — with WebIDs of the form
https://<identity-host>/<handle>#me. Those documents answer GET and HEAD only, are
publicly readable, and carry no .acl link.
Enable this when you do not want the Solid-OIDC trust root inside the pod. A WebID
document tells every resource server which issuers may mint tokens for that WebID, so an
in-pod, owner-writable, WAC-governed WebID is one over-broad acl:default grant away from
letting someone else's grant rewrite that trust root.
Two consequences to design around:
- The reserved
/.identity/**path is refused with404on the LDP surface for every method, whether or not the flag is set. You cannot create, read, or write an ACL for anything under it. That is deliberate, not a gap. - With the flag on, the conformance seed mints identity-host WebIDs whose documents hold
the
solid:oidcIssuerandpim:storagestatements, and demotes the in-pod/{u}/profile/cardto a user-editable profile that carries neither. Read identity from the WebID document, not from the in-pod card.
Use ordinary HTTP methods against resource IRIs:
# Read an RDF resource.
curl -H 'Accept: text/turtle' https://solid.example/alice/profile/card
# Replace or create a resource. Add fresh Authorization and DPoP headers.
curl -X PUT \
-H 'Authorization: DPoP ACCESS_TOKEN' \
-H 'DPoP: FRESH_REQUEST_BOUND_PROOF' \
-H 'Content-Type: text/turtle' \
--data-binary '<#me> <http://xmlns.com/foaf/0.1/name> "Alice" .' \
https://solid.example/alice/profile/card
# Create a child below a container; use the returned Location.
curl -i -X POST \
-H 'Authorization: DPoP ACCESS_TOKEN' \
-H 'DPoP: FRESH_REQUEST_BOUND_PROOF' \
-H 'Content-Type: text/turtle' \
-H 'Slug: note' \
--data-binary '<#it> <http://purl.org/dc/terms/title> "Note" .' \
https://solid.example/alice/GET and HEAD read resources. PUT creates or replaces, POST mints a
collision-resistant child IRI below a container, PATCH accepts supported
Solid/SPARQL patch forms, and DELETE removes a resource or an empty
container. Use If-Match and If-None-Match for conditional mutations.
Container reads include generated ldp:contains triples.
Use a trailing slash for containers. A resource and container cannot coexist at
slash-equivalent paths. ACL resources use the sibling .acl convention and
are themselves protected by acl:Control.
Request Turtle or JSON-LD:
curl -H 'Accept: text/turtle' https://solid.example/alice/profile/card
curl -H 'Accept: application/ld+json' https://solid.example/alice/profile/cardThe JSON-LD reader also recognizes the canonical expanded and compacted
profile parameters and echoes the honored profile in Content-Type. Its
compacted form is a local, context-free structural compaction: it does not
fetch a remote context and is not the full W3C Compaction Algorithm. See
skills/jsonld/SKILL.md for the exact profile behavior.
An unknown Accept media type falls back to the Solid default
text/turtle. Explicitly refusing every producible type with q=0 returns
406 Not Acceptable. Non-RDF resources preserve their binary media type and
support byte-range reads.
The default sparql-endpoint feature exposes authenticated GET and POST
SPARQL Protocol query operations:
curl -G https://solid.example/sparql \
-H 'Authorization: DPoP ACCESS_TOKEN' \
-H 'DPoP: FRESH_REQUEST_BOUND_PROOF' \
-H 'Accept: application/sparql-results+json' \
--data-urlencode 'query=SELECT ?g ?s ?p ?o WHERE { GRAPH ?g { ?s ?p ?o } }'For POST, send either application/sparql-query or
application/x-www-form-urlencoded. The v1 route supports SELECT, ASK,
and CONSTRUCT; it rejects DESCRIBE and SPARQL Update. SELECT and ASK
return application/sparql-results+json; CONSTRUCT returns
application/n-triples.
The endpoint assembles one named graph per RDF resource that the caller may
read under WAC. The default graph is empty. Failed enumeration,
authorization, body reads, or RDF parsing exclude a resource in the safe
direction. Protocol default-graph-uri and named-graph-uri parameters may
select from that authorized dataset; they cannot make an unreadable resource
visible.
Several behaviours here implement an external specification, and the spec is the contract — where this server and the spec disagree, treat the spec as right and the server as the defect. Write integration code against the spec, and pin the same revision the server pins:
- Solid-OIDC access tokens and DPoP proofs: baseline verification on the normal
cache-miss path is delegated to the pinned
solid-oidc-verifiergit dependency (seecrates/sparq-lws-core/Cargo.tomlfor the exact revision), so what a token or proof must look like is whatever that revision enforces. Delegated is not pass-through, though, and integrators should know the two places this crate participates. On a verified-token-cache hit the token signature and claims are not re-verified (they were checked once on the miss, and the entry is keyed by the token and expires atmin(token exp, a shorter validation-freshness TTL)), while the fresh DPoP proof is verified locally on every request — proof signature,htm/htu/iat,ath, thejtireplay mark against the same shared replay store, and thecnf.jktbinding — orchestrated from the verifier's own public primitives (crates/sparq-lws-core/src/auth_cache.rs). Separately, the server layers opt-in proof-of-possession tiers on top of an already-verified token: RFC 8705 mTLS cert-bound tokens, and DPoP-SK below. Both are off by default (crates/sparq-lws-core/src/auth.rs). - DPoP-SK, the sender-key proof-of-possession tier, follows the
DPoP-SK profile. The spec's Appendix-A worked
example is executed as a test vector in
crates/sparq-lws-core/src/pop/sk/derive.rs, so a drift between the spec's derivation and this implementation fails a test rather than going unnoticed. Derive session keys per the spec, not by reading the Rust. - WAC, LDP, and the Solid Protocol govern the access-control and resource surfaces above; the sections of this skill describe how this server exposes them, not what they require.
For the design decisions behind the identity host, the in-process engine backend, the
public-read fast path, and the existence-non-disclosure guards, read
research/lws-design-records.md. It is
reconstructed from the code and cites it line by line; it also maps the source-repo
decisions/ and docs/design/ paths that this crate's doc-comments still reference.
GET /livezandGET /readyzare unauthenticated probes.- Do not expose the development seed modes or loopback auth escape hatch in production.
- Scale authenticated instances with a shared replay store only via the
default-off
redis-replayfeature andSOLID_SERVER_REPLAY_REDIS_URL; otherwise DPoP replay state is per-instance. - Use
crates/sparq-lws-core/README.mdandsrc/main.rsas the authoritative inventory for advanced cache, transport, identity-host, reconciliation, and proof-of-possession environment variables. - Use
skills/usage-control-policy/SKILL.mdfor the default-offodrl-authzread/query gate. - Use
skills/access-control/SKILL.md§ LWS-server admission seam for the default-offtrust-graphfeature. It adds the library functionauthz::trust_admit::trust_admit_verdict, which is deliberately not wired into the request pipeline, so enabling it changes no request's outcome.