This specification defines an HTTP endpoint through which a client can evaluate read-only SPARQL queries over the RDF content of a Solid storage (a Pod), receiving results computed exclusively from the resources the requesting agent is authorized to read. The endpoint implements the SPARQL 1.1 Protocol query operation [[SPARQL11-PROTOCOL]]; requests use the Solid server's authentication layer, as for any other Solid resource request [[SOLID-PROTOCOL]]; and authorization is the storage's existing access control system ([[WAC]] or [[ACP]]), enforced by restricting the queried RDF dataset — one named graph per readable RDF resource, with an empty default graph — before query evaluation. The specification defines the mapping from Pod content to the queried dataset, the construction of the per-request authorized dataset, an opt-in union default graph mode, and a set of existence-non-disclosure invariants ensuring that a query can never reveal the existence or contents of a resource the requester cannot read.
This document specifies a read interface layered on Solid's existing authentication and authorization substrate. It does not define or require a particular authentication mechanism, does not define a new authorization model, and does not define a new wire protocol.
This draft was prepared with the assistance of AI systems operated on behalf of the editor (design: Claude Opus 4.8; Editor's Draft and final design resolutions: Claude Fable 5). The editor is responsible for all content. Contributions and issues are welcome on the GitHub repository.
The Solid Protocol [[SOLID-PROTOCOL]] gives agents fine-grained, resource-level access to RDF data in a storage, but its read interface is resource-at-a-time: a client that wants to answer a question spanning many resources must traverse and fetch each one (for example via client-side link-traversal query engines). Solid has no ratified server-side query surface. This is costly for clients, for servers, and for networks, and it makes whole classes of applications — search, aggregation, cross-resource joins over one's own data — needlessly hard to build.
SPARQL [[SPARQL11-QUERY]] is the W3C standard query language for RDF, with a stable, universally implemented HTTP protocol [[SPARQL11-PROTOCOL]]. This specification brings the two together: a per-storage SPARQL query endpoint whose observable behaviour is indistinguishable from evaluating the query over only the resources the requester could have fetched individually. A conforming endpoint adds query capability without adding any disclosure capability.
This specification composes with, and does not modify, the Solid Protocol [[SOLID-PROTOCOL]], Web Access Control [[WAC]], and the Access Control Policy language [[ACP]]. Authentication on the query endpoint is delegated to the authentication layer the Solid server uses for other resource requests; this specification neither defines nor mandates that layer's mechanism. Authorization decisions are made by the storage's existing access control system; this specification consumes those decisions — it introduces no new access modes, no new policy language, and no new identity mechanism. The single governing principle is GET-equivalence: a triple is visible to a query if and only if the requester is authorized to read the resource that asserts it.
This section defines the terms used in this specification. Terms defined by RDF 1.1 [[RDF11-CONCEPTS]], SPARQL 1.1 [[SPARQL11-QUERY]] [[SPARQL11-PROTOCOL]], and the Solid Protocol [[SOLID-PROTOCOL]] are used with their definitions from those specifications; the most load-bearing are restated here for convenience.
GET request on that resource by that agent. For
resources governed by [[WAC]] this is the acl:Read access mode resolved
through the acl:accessTo/acl:default walk; for auxiliary
resources it is whatever access the governing specification requires to read them (for
example, reading a WAC ACL resource requires acl:Control); for [[ACP]] it is
the Read access mode granted by the applicable policies.
@id and @graph
[[JSON-LD11]].
This specification mints terms in the namespace
http://www.w3.org/ns/solid/sparql#, written below with the prefix
solid-sparql:.
| Term | Type | Definition |
|---|---|---|
solid-sparql:endpoint |
Link relation (extension relation type per [[RFC8288]]) and RDF property | Relates a storage (or a resource within it) to the query service for that storage. See . |
solid-sparql:union-default-graph |
Reserved graph IRI | When used as a default-graph IRI of a query's dataset description, requests union default graph mode for that query. See . |
solid-sparql:UnionDefaultGraphOptIn |
Instance of sd:Feature [[SPARQL11-SERVICE-DESCRIPTION]] |
Advertises, in a service description, that the service implements the opt-in union default graph mode. See . |
These terms are proposed for the Solid vocabulary namespace and are subject to Solid Community Group adoption. Until the namespace document is published, implementations should treat the IRIs as defined by this specification.
Other namespace prefixes used in this document:
| Prefix | Namespace IRI |
|---|---|
sd: | http://www.w3.org/ns/sparql-service-description# |
acl: | http://www.w3.org/ns/auth/acl# |
foaf: | http://xmlns.com/foaf/0.1/ |
dct: | http://purl.org/dc/terms/ |
schema: | https://schema.org/ |
This specification defines the following conformance classes:
A server MAY expose a query service for a storage. A query service MUST be scoped to exactly one storage: its authorized dataset is derived exclusively from the resources of that storage. A server MUST NOT expose, through a single query service, graphs derived from resources of more than one storage.
The IRI of the query service is determined by the server. It MAY be any IRI; it need not be inside the storage's URI space.
A common convention is <storage root>sparql — for example
https://alice.pod.example/sparql for the storage
https://alice.pod.example/. Clients discover the endpoint via
and must not assume this convention.
A server exposing a query service MUST advertise it by including, in responses to
GET and HEAD requests on the storage's root container, an HTTP
Link header [[RFC8288]] whose target is the query service IRI and whose
relation type is http://www.w3.org/ns/solid/sparql#endpoint.
HTTP/1.1 200 OK Link: <https://alice.pod.example/sparql>; rel="http://www.w3.org/ns/solid/sparql#endpoint"
A server SHOULD also assert the endpoint in the storage description resource [[SOLID-PROTOCOL]], using the relation IRI as an RDF property:
@prefix solid-sparql: <http://www.w3.org/ns/solid/sparql#> . <https://alice.pod.example/> solid-sparql:endpoint <https://alice.pod.example/sparql> .
A server MAY include the Link header in responses for other resources of the
storage.
The advertisement of the query service, like the service description (), MUST NOT vary in a way that discloses the existence or contents of resources the requester cannot read.
A query service MUST implement the query operation of the SPARQL 1.1 Protocol [[SPARQL11-PROTOCOL]], including all three of its bindings:
query URI query parameter;application/x-www-form-urlencoded;application/sparql-query.
A query service MUST support the SPARQL 1.1 Query language [[SPARQL11-QUERY]] for the
query operand, including the SELECT, CONSTRUCT,
ASK, and DESCRIBE query forms.
A query service MUST support the default-graph-uri and
named-graph-uri protocol parameters. As required by [[SPARQL11-PROTOCOL]], a
dataset description supplied via protocol parameters takes precedence over one supplied in
the query string (FROM/FROM NAMED). All dataset descriptions,
however supplied, are resolved through the authorization rules of
; a query service MUST NOT reject a request because its
dataset description references a graph the requester cannot read
().
A query service MUST NOT execute SPARQL Update operations (see ).
SPARQL 1.2 [[SPARQL12-PROTOCOL]] [[SPARQL12-QUERY]] is, at the protocol layer, a
backward-compatible superset of SPARQL 1.1: the same three bindings and the same core media
types, adding an optional version media-type parameter and RDF-1.2-aware
payloads [[RDF12-CONCEPTS]].
A query service MAY additionally implement the SPARQL 1.2 Protocol query operation,
including accepting the version media-type parameter and producing RDF 1.2
constructs (such as triple terms) in results. A query service MUST NOT require any SPARQL
1.2 feature: every capability required for conformance to this specification is
expressible in SPARQL 1.1.
Query results are a function of the requester. A query service MUST prevent a response computed for one requester from being served to a different requester by a shared cache.
In practice this means emitting Cache-Control: private (or
no-store) on query responses, together with an appropriate
Vary header (e.g. Vary: Authorization, Accept) [[RFC9110]].
A SPARQL query is evaluated against an RDF dataset: one default graph and zero or more named graphs [[SPARQL11-QUERY]] [[RDF11-CONCEPTS]]. This section defines how a storage's content determines that dataset; defines how it is restricted per request.
Each RDF source in the storage contributes exactly one named graph to the queried dataset:
Containers are RDF sources; a container's resource graph comprises its representation as
served on GET, including server-managed containment triples. Auxiliary
resources that are RDF sources (for example WAC ACL resources) likewise contribute resource
graphs, subject to their own read-access rules ().
Resources that are not RDF sources (binary/non-RDF resources) MUST NOT contribute a graph to the dataset.
Because the graph name is the resource IRI, the set of named graph names is exactly the set of (RDF source) resource IRIs — which is why enumerating graph names is equivalent to listing resources, and why every disclosure rule in this specification is enforced at the graph-name layer.
The standing default graph of the queried dataset MUST be empty. A query that does not
supply a dataset description (no FROM/FROM NAMED, no
default-graph-uri/named-graph-uri) and does not invoke
union default graph mode is therefore evaluated against a dataset whose default
graph contains no triples: a bare basic graph pattern such as
{ ?s ?p ?o } yields zero solutions.
All resource content is exposed through named graphs. Cross-resource querying is
explicit: via GRAPH patterns (), via a per-query
dataset description (), or via the opt-in
union default graph mode ().
A Turtle document [[TURTLE]] serializes a single RDF graph; for an RDF source represented in Turtle (or any other graph-denoting syntax, such as N-Triples or RDFa), the resource graph is simply that graph.
A JSON-LD document, by contrast, serializes an RDF dataset [[JSON-LD11]]
[[JSON-LD11-API]]: a node object carrying both @id and @graph
denotes an inner named graph, so a single resource's representation may deserialize
to default-graph triples plus named graphs. This breaks the one-resource-one-graph
bijection of unless resolved. This specification resolves
it by flattening:
When an RDF source's representation deserializes to an RDF dataset, the resource graph MUST be the RDF graph comprising every triple asserted in any graph of that dataset — the default graph and all inner named graphs — with blank node identity preserved as in the deserialized dataset. The inner graph names are discarded as graph names; triples whose subject or object is an inner graph name are retained as ordinary triples.
A server MUST NOT expose an inner named graph name as a named graph of the queried dataset, and MUST NOT contribute triples from one resource's representation to any graph other than that resource's own resource graph.
Promoting inner graph names to first-class SPARQL named graphs would let the content of one
resource choose arbitrary graph names — including names equal to other resources'
IRIs. Two resources declaring the same inner @id would commingle under one
graph name that the access control system decides as a unit, and a hostile document could
inject triples into the graph name of a resource its author cannot write. It would also
break the invariant that every named graph corresponds to exactly one resource and hence
exactly one access control verdict (). Flattening is
collision-free and keeps that invariant airtight.
Flattening affects only what a SPARQL query can see. The stored representation is
untouched: a GET on the resource returns the original document, inner graphs
and all. What is lost to queries is only the association between an inner graph name and
its triples — a structure that is rare in practice in Pod data. A compatible, opt-in
fidelity mode that preserves this association as data is sketched in
.
This section is a consequence of SPARQL 1.1 semantics [[SPARQL11-QUERY]] applied to the dataset of this specification; it is stated normatively because implementations MUST preserve these outcomes exactly (they are load-bearing for ).
SPARQL has no FROM * or FROM ?g: FROM and
FROM NAMED take fixed IRIs. The "query over all graphs" capability is the
GRAPH ?g pattern, whose graph variable, on a query service, ranges over
exactly the named graphs of the authorized dataset:
{ ?s ?p ?o } — matches the (empty) default graph: zero
solutions.
{ GRAPH ?g { ?s ?p ?o } } — matches every triple in every readable named
graph, binding ?g to each graph name.
GRAPH block must be satisfied within the same graph:
{ GRAPH ?g { ?p foaf:knows ?f . ?f foaf:name ?n } }
GRAPH blocks with distinct
graph variables allow the triples to come from different graphs (including, as a special
case, the same graph), joined on shared variables:
{ GRAPH ?g1 { ?p foaf:knows ?f } GRAPH ?g2 { ?f foaf:name ?n } }
SELECT ?f ?n FROM <A> FROM <B> WHERE { ?p foaf:knows ?f . ?f foaf:name ?n }
An unreadable or absent FROM target contributes nothing
().
Blank nodes are scoped to a single resource's representation: distinct resources never share blank nodes, so cross-graph joins can only join on IRIs and literals. Worked examples, including the intra- versus cross-graph contrast on a concrete dataset, are given in .
This section is informative. It names, but does not specify, a future compatible extension.
A future version of this specification is expected to define an opt-in fidelity mode that preserves inner-named-graph structure as data inside the resource graph, using RDF 1.2 triple terms (RDF-star) [[RDF12-CONCEPTS]]: each triple of an inner named graph would additionally be described by an annotation associating it with the inner graph name, along the lines of:
GRAPH <https://alice.pod.example/notes/meeting> {
<#decision1> schema:text "Adopt the proposal" .
<< <#decision1> schema:text "Adopt the proposal" >>
solid-sparql:inGraph <#minutes> . # predicate name illustrative
}
This design keeps every property that makes flattening safe — no additional SPARQL named graphs, no cross-resource name collisions, one access-control verdict per resource — while making the inner-graph association queryable by clients that need it. It was chosen over promoting inner graphs to namespaced first-class graphs because it needs no name-rewriting or provenance table. It is deferred from this version because RDF-star querying is a SPARQL 1.2 feature, and this specification deliberately requires only SPARQL 1.1 (): pulling reification into the core would smuggle a normative RDF 1.2 dependency into a specification whose protocol layer refuses one. When defined, the fidelity mode will be an additive, per-query or per-service opt-in; documents already stored will need no migration, since flattening never alters stored representations. Any such future mode must preserve the prohibition of raw inner-graph-name promotion.
A request to the query service is an ordinary HTTP request to the Solid server. The server MUST process authentication for a query-service request through the Solid Protocol authentication layer it uses for ordinary resource requests [[SOLID-PROTOCOL]]. This specification does not define or mandate any particular authentication mechanism.
A query service MAY refuse service categorically to classes of requester (for example,
requiring authentication for all queries, or disabling the endpoint entirely), using an
ordinary 401/403 response. Such refusal MUST be uniform: it MUST
NOT depend on the query text, on the dataset description, or on the existence, contents, or
access rules of any resource — otherwise the refusal itself becomes a disclosure oracle
().
A query service MUST NOT disclose, by any observable behaviour, the existence, non-existence, metadata, or contents of any resource outside the requester's authorized dataset. Unreadable resources and non-existent resources MUST be observationally equivalent through the query interface.
Specifically, for every query request:
CONSTRUCT or
DESCRIBE result, and no ASK answer may be derived from a graph
outside the authorized dataset.
GRAPH ?g MUST NOT produce a binding for, and
no result may otherwise reveal, the name of any graph outside the authorized dataset.
COUNT, SUM,
GROUP_CONCAT, …) MUST be computed over solutions from the authorized dataset
only; a count may never differ because of unreadable content.
403-versus-404 distinction inside the dataset: dataset
references that cannot be honoured are silently absent
(), never errors.
Invariants 1–5 are satisfied structurally — not by output scrubbing — when the implementation restricts the dataset at the graph-name layer before evaluation, as requires: an engine that never touches unreadable graphs cannot leak them through bindings, counts, errors, limits, or (to first order) timing. This is why post-filtering is non-conforming even when its final output is identical. The residual timing channel addressed by invariant 6 concerns shared infrastructure (for example, index structures whose access cost varies with whole-store size); see .
Result serialization follows the SPARQL 1.1 family of result formats, negotiated via HTTP content negotiation [[RFC9110]].
SELECT queries, a query service MUST support
application/sparql-results+json [[SPARQL11-RESULTS-JSON]],
application/sparql-results+xml [[RDF-SPARQL-XMLRES]],
text/csv, and text/tab-separated-values
[[SPARQL11-RESULTS-CSV-TSV]].
ASK queries, a query service MUST support
application/sparql-results+json and
application/sparql-results+xml.
CONSTRUCT and DESCRIBE queries, a query service MUST support
text/turtle [[TURTLE]] and application/ld+json [[JSON-LD11]],
mirroring the RDF representations required of Solid servers [[SOLID-PROTOCOL]]; it MAY
support additional RDF serializations.
Accept header, a query service SHOULD default to
application/sparql-results+json for SELECT/ASK and
text/turtle for CONSTRUCT/DESCRIBE.
On a GET request to the query service IRI with no query parameters, the service
SHOULD return a SPARQL 1.1 Service Description [[SPARQL11-SERVICE-DESCRIPTION]] describing
the endpoint's capabilities: supported language(s), result formats, features, and
(optionally) resource limits.
Because graph names are resource IRIs (), a graph inventory is a resource listing. Accordingly:
sd:namedGraph, sd:availableGraphs,
sd:defaultDataset, or any other property.
GRAPH ?g. If a service does include an inventory, it MUST be
filtered, per request, to graphs readable by the requester.
sd:UnionDefaultGraph feature: its
defined meaning — that the dataset's default graph is the union of the named graphs — would
falsely describe an endpoint whose standing default graph is empty
().
sd:feature solid-sparql:UnionDefaultGraphOptIn; a service not implementing the
mode MUST NOT advertise that feature.
@prefix sd: <http://www.w3.org/ns/sparql-service-description#> .
@prefix solid-sparql: <http://www.w3.org/ns/solid/sparql#> .
<https://alice.pod.example/sparql> a sd:Service ;
sd:endpoint <https://alice.pod.example/sparql> ;
sd:supportedLanguage sd:SPARQL11Query ;
sd:resultFormat
<http://www.w3.org/ns/formats/SPARQL_Results_JSON> ,
<http://www.w3.org/ns/formats/SPARQL_Results_XML> ,
<http://www.w3.org/ns/formats/SPARQL_Results_CSV> ,
<http://www.w3.org/ns/formats/SPARQL_Results_TSV> ,
<http://www.w3.org/ns/formats/Turtle> ,
<http://www.w3.org/ns/formats/JSON-LD> ;
sd:feature solid-sparql:UnionDefaultGraphOptIn .
# No graph inventory; no sd:UnionDefaultGraph.
SPARQL evaluation cost is unbounded in general: property paths, large joins, and
OPTIONAL-heavy queries can consume arbitrary CPU, memory, and I/O. A query
service MAY impose resource limits — including evaluation timeouts, complexity or
cardinality bounds, result-size caps, and per-requester rate limits. When a limit causes a
query to be aborted, the service SHOULD respond with a 5xx status (for
example, 503 Service Unavailable, with Retry-After where
appropriate) or with a documented error response; it MUST honour the non-disclosure
invariants in doing so (, invariant 5).
The central threat this specification defends against is the read oracle: any
channel through which a query's observable outcome varies with data the requester cannot
read. The class includes result bindings, ASK answers, aggregate counts, graph
enumeration, dataset-clause errors, status-code differences (403 versus
404), resource-limit behaviour, and evaluation timing. The architecture closes
the class structurally: authorization is applied to graph names before evaluation,
so unreadable data never enters the computation whose outcome the attacker observes
().
Residual channels are those shared between requests at the infrastructure layer — cache occupancy, index sizes, storage latency correlated with whole-store contents. These are qualitatively the same channels present in any multi-tenant HTTP server and are mitigated with the usual tools (constant-time-ish paths where cheap, noise, rate limiting); invariant 6 of makes their mitigation a SHOULD.
SERVICE)
The SPARQL 1.1 SERVICE keyword instructs the server to contact remote
endpoints, creating a server-side request-forgery (SSRF) and confused-deputy surface. A
query service MAY reject queries containing SERVICE, and SHOULD reject them by
default. A service that does evaluate SERVICE patterns MUST NOT forward the
requester's credentials (or any server credential) to the remote endpoint, and SHOULD apply
standard SSRF protections (deny-by-default network egress, address-family and DNS
validation) to the outbound request.
GET on the
container does. This is existing Solid behaviour, restated here because containment
triples become joinable at query time.
This specification defines a read-only interface. A query service MUST NOT execute SPARQL
Update operations [[SPARQL11-PROTOCOL]]. A request that is recognizably an update request
MUST be refused without side effects: a service SHOULD respond
415 Unsupported Media Type to a direct POST of
application/sparql-update, and 400 Bad Request to a form request
carrying an update parameter.
A future specification may define an update operation on the same substrate. The expected
mode mapping, recorded here so the design space is visible, is per target graph:
WHERE patterns require Read on every graph they consult;
INSERT requires Append on each target graph; DELETE
requires Write on each target graph. The principal new hazard is the
DELETE/WHERE read oracle — an update whose effect
(which triples got deleted, or whether anything changed) reveals the outcome of a
WHERE evaluation the requester could not have performed as a query — which
must be closed with the same before-evaluation dataset restriction used here, applied to
the WHERE clause. Update is deferred until that analysis is complete.
The scenarios below restate the normative requirements of this specification in testable
form; the referenced sections are authoritative. Except where stated, assume the example
storage of : requester R can read
/profile/card and /contacts/bob but not
/private/journal, and /no-such-resource does not exist.
SELECT * WHERE { ?s ?p ?o }
with no dataset description returns zero solutions, whatever the storage contains.
()
SELECT DISTINCT ?g WHERE { GRAPH ?g { ?s ?p ?o } } is the IRI of an RDF source
readable by R. (,
)
</private/journal>. ()
SELECT * FROM </private/journal> WHERE { ?s ?p ?o } and
SELECT * FROM </no-such-resource> WHERE { ?s ?p ?o } both return
200 with zero solutions and indistinguishable response structure — no error,
no 403/404. (,
)
ASK { GRAPH </private/journal> { ?s ?p ?o } } and
ASK { GRAPH </no-such-resource> { ?s ?p ?o } } both answer
false. ()
SELECT (COUNT(*) AS ?n) WHERE { GRAPH ?g { ?s ?p ?o } } yields the same
?n whether or not /private/journal exists.
()
acl:Read but not acl:Control on /x sees
graph </x> but not graph </x.acl>.
()
GRAPH </notes/meeting>, and
ASK { GRAPH </notes/meeting#minutes> { ?s ?p ?o } } answers
false. ()
?g binding or is matchable as a
GRAPH name. ()
solid-sparql:UnionDefaultGraphOptIn: scenario 2 still returns zero solutions,
while the same bare-BGP query with
FROM <http://www.w3.org/ns/solid/sparql#union-default-graph> (or the
equivalent default-graph-uri parameter) returns solutions from all readable
graphs. ()
FROM NAMED <http://www.w3.org/ns/solid/sparql#union-default-graph> adds
no named graph, and ?g never binds to the reserved IRI.
()
sd:UnionDefaultGraph. ()
application/sparql-update is
refused (e.g. 415) with no side effects; a form POST with an
update parameter is refused (e.g. 400).
()
The storage https://alice.pod.example/ contains three RDF sources. The
requester R is authorized to read /profile/card and
/contacts/bob, but not /private/journal.
# Graph <https://alice.pod.example/profile/card> (readable by R)
GRAPH <https://alice.pod.example/profile/card> {
<https://alice.pod.example/profile/card#me>
foaf:name "Alice" ;
foaf:knows <https://bob.pod.example/profile/card#me> .
}
# Graph <https://alice.pod.example/contacts/bob> (readable by R)
GRAPH <https://alice.pod.example/contacts/bob> {
<https://bob.pod.example/profile/card#me> foaf:name "Bob" .
}
# https://alice.pod.example/private/journal exists but is NOT readable by R:
# it contributes NO graph to R's authorized dataset.
SELECT ?s ?p ?o WHERE { ?s ?p ?o }
# → no solutions: the standing default graph is empty.
SELECT DISTINCT ?g WHERE { GRAPH ?g { ?s ?p ?o } }
# → ?g = <https://alice.pod.example/profile/card>
# ?g = <https://alice.pod.example/contacts/bob>
# /private/journal is absent — indistinguishable from not existing.
A readable resource whose graph is empty (for example, an empty Turtle document) produces
no binding here — ordinary SPARQL semantics, since GRAPH ?g requires a
matching triple. That is not a disclosure issue; it is the same behaviour for every
requester who can read the resource.
Alice's foaf:knows triple lives in /profile/card, but Bob's name
lives in /contacts/bob. A join written inside a single GRAPH
block requires both triples in the same graph, and therefore fails:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?f ?n WHERE {
GRAPH ?g { ?p foaf:knows ?f . ?f foaf:name ?n }
}
# → no solutions: no single graph contains both triples.
Separate GRAPH blocks with distinct graph variables join across graphs on the
shared variable ?f:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?f ?n WHERE {
GRAPH ?g1 { ?p foaf:knows ?f }
GRAPH ?g2 { ?f foaf:name ?n }
}
# → ?f = <https://bob.pod.example/profile/card#me>, ?n = "Bob"
# (?g1 = <…/profile/card>, ?g2 = <…/contacts/bob>)
Alternatively, merge chosen graphs into the query's default graph with FROM,
so bare patterns join across them:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?f ?n
FROM <https://alice.pod.example/profile/card>
FROM <https://alice.pod.example/contacts/bob>
WHERE { ?p foaf:knows ?f . ?f foaf:name ?n }
# → ?f = <https://bob.pod.example/profile/card#me>, ?n = "Bob"
The resource https://alice.pod.example/notes/meeting is stored as JSON-LD and
uses an inner named graph:
{
"@context": {
"@vocab": "https://schema.org/",
"dct": "http://purl.org/dc/terms/"
},
"@graph": [
{
"@id": "https://alice.pod.example/notes/meeting",
"dct:title": "Meeting notes"
},
{
"@id": "https://alice.pod.example/notes/meeting#minutes",
"@graph": [
{
"@id": "https://alice.pod.example/notes/meeting#decision1",
"text": "Adopt the proposal"
}
]
}
]
}
The representation deserializes to a dataset: one default-graph triple plus an inner named
graph <…#minutes>. Flattening ()
collapses it into the single resource graph:
GRAPH <https://alice.pod.example/notes/meeting> {
<https://alice.pod.example/notes/meeting>
<http://purl.org/dc/terms/title> "Meeting notes" .
<https://alice.pod.example/notes/meeting#decision1>
<https://schema.org/text> "Adopt the proposal" .
}
ASK { GRAPH <https://alice.pod.example/notes/meeting#minutes> { ?s ?p ?o } }
# → false: inner graph names are never named graphs of the dataset.
SELECT ?text WHERE {
GRAPH <https://alice.pod.example/notes/meeting> {
?d <https://schema.org/text> ?text
}
}
# → ?text = "Adopt the proposal" — the inner graph's triples are in the resource graph.
Contrast the forbidden raw-preservation behaviour (): had
<…#minutes> become a first-class graph, a hostile document at
/evil declaring an inner @id of
https://alice.pod.example/profile/card could have injected triples under the
profile's graph name.
On a service advertising solid-sparql:UnionDefaultGraphOptIn:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?f ?n
FROM <http://www.w3.org/ns/solid/sparql#union-default-graph>
WHERE { ?p foaf:knows ?f . ?f foaf:name ?n }
# → ?f = <https://bob.pod.example/profile/card#me>, ?n = "Bob"
# The default graph for THIS query is the merge of all graphs R can read;
# a bare BGP now joins across resources.
GET /sparql?query=SELECT%20%3Ff%20%3Fn%20WHERE%20%7B%20%3Fp%20foaf%3Aknows%20%3Ff%20.%20%3Ff%20foaf%3Aname%20%3Fn%20%7D&default-graph-uri=http%3A%2F%2Fwww.w3.org%2Fns%2Fsolid%2Fsparql%23union-default-graph HTTP/1.1 Host: alice.pod.example Accept: application/sparql-results+json
The same query without the reserved IRI still returns zero solutions — the standing default graph remains empty, always.
This specification builds on the work of the W3C Solid Community Group and on the SPARQL and
RDF specifications of the W3C. The dataset-restriction-before-evaluation architecture and its
existence-non-disclosure invariants were developed and validated in the
solid-server-rs
implementation.