Sibling of #389 (array-context) and #390 (@type: '@json'). Surfaced when checking whether test.solid.social's JSON-LD profile and its conneg-converted Turtle form match.
Symptom
When a JSON-LD profile contains a predicate whose values are inline objects (rather than flat IRI references), the conneg-emitted Turtle drops:
- The predicate triple connecting the subject to the inline objects
- The inline objects' own triples (their
@id, @type, attributes)
The authentication predicate survives because it points at a flat IRI. The verificationMethod predicate disappears because its values are full inline-object descriptions.
Reproduction
The on-disk profile at test.solid.social/profile/card.jsonld contains:
{
"@context": { "...": "...", "verificationMethod": { "@id": "cid:verificationMethod", "@container": "@set" }, "publicKeyMultibase": { "@id": "cid:publicKeyMultibase" }, ... },
"@id": "https://test.solid.social/profile/card.jsonld#me",
"verificationMethod": [{
"id": ".../card.jsonld#nostr-key-1",
"type": "Multikey",
"controller": ".../card.jsonld#me",
"publicKeyMultibase": "fe70102de7ec..."
}],
"authentication": [".../card.jsonld#nostr-key-1"]
}
Asking JSS for Turtle:
curl -H 'Accept: text/turtle' https://test.solid.social/profile/card.jsonld
returns (excerpt):
<https://test.solid.social/profile/card.jsonld#me> a foaf:Person, schema:Person;
...
<https://www.w3.org/ns/cid/v1#controller> <.../card.jsonld#me>;
<https://www.w3.org/ns/cid/v1#service> <.../card.jsonld#oidc>;
<https://www.w3.org/ns/cid/v1#authentication> <.../card.jsonld#nostr-key-1>.
<https://test.solid.social/profile/card.jsonld#oidc> a <https://www.w3.org/ns/lws#OpenIdProvider>;
<https://www.w3.org/ns/cid/v1#serviceEndpoint> <https://solid.social/>.
Note:
- ❌ No
cid:verificationMethod predicate on #me at all
- ❌ No description of
#nostr-key-1 (no a Multikey, no controller, no publicKeyMultibase)
- ✅
cid:authentication <.../card.jsonld#nostr-key-1> survives (flat IRI)
- ✅
cid:service <.../card.jsonld#oidc> survives, AND the #oidc resource IS described (because it's referenced as a flat IRI from the subject AND its description is at the top level of the doc, not nested inside service)
So the rule seems to be: predicates whose values are inline objects (vs. flat IRI references with the resource described at the document root) get dropped.
Why this matters
Likely root cause
src/rdf/turtle.js's emitter probably walks the JSON-LD object's top-level keys and emits a triple per predicate-value pair, but doesn't recursively descend into inline-object values to:
- Emit the predicate referencing the inline object's
@id
- Recursively emit the inline object's own triples
Compare to how service works (and survives): the JSON-LD likely has service: [".../card.jsonld#oidc"] as a flat-IRI list AND a top-level #oidc block describing the service endpoint. Two parallel descriptions. The Multikey VM is only declared as an inline object inside verificationMethod — there's no top-level #nostr-key-1 block to fall back to.
Possible fixes
A. Hoist + flatten on serialize: when emitting Turtle, replace inline objects with flat IRI references AND emit a top-level resource block per inline object. This is the standard JSON-LD → flat-N-Quads transformation.
B. Recursive emission: walk inline-object values, emit them as anonymous (or named, if @id present) resource blocks alongside the parent.
C. Switch to a real JSON-LD library: jsonld.js (digitalbazaar) handles this correctly out of the box. JSS's deliberate choice to avoid remote-context fetches (#389) is preserved by passing a pre-resolved local context map.
(C) is the most robust but biggest change. (A) or (B) would unblock the immediate use case.
Acceptance test
After the fix, the Turtle conversion of the test profile must contain:
<.../card.jsonld#me>
cid:verificationMethod <.../card.jsonld#nostr-key-1>;
cid:authentication <.../card.jsonld#nostr-key-1>.
<.../card.jsonld#nostr-key-1>
a cid:Multikey;
cid:controller <.../card.jsonld#me>;
cid:publicKeyMultibase "fe70102de7ec...".
(Whitespace + prefix-vs-IRI formatting may vary; the triple set must match.)
Refs
Sibling of #389 (array-context) and #390 (
@type: '@json'). Surfaced when checking whethertest.solid.social's JSON-LD profile and its conneg-converted Turtle form match.Symptom
When a JSON-LD profile contains a predicate whose values are inline objects (rather than flat IRI references), the conneg-emitted Turtle drops:
@id,@type, attributes)The
authenticationpredicate survives because it points at a flat IRI. TheverificationMethodpredicate disappears because its values are full inline-object descriptions.Reproduction
The on-disk profile at
test.solid.social/profile/card.jsonldcontains:{ "@context": { "...": "...", "verificationMethod": { "@id": "cid:verificationMethod", "@container": "@set" }, "publicKeyMultibase": { "@id": "cid:publicKeyMultibase" }, ... }, "@id": "https://test.solid.social/profile/card.jsonld#me", "verificationMethod": [{ "id": ".../card.jsonld#nostr-key-1", "type": "Multikey", "controller": ".../card.jsonld#me", "publicKeyMultibase": "fe70102de7ec..." }], "authentication": [".../card.jsonld#nostr-key-1"] }Asking JSS for Turtle:
curl -H 'Accept: text/turtle' https://test.solid.social/profile/card.jsonldreturns (excerpt):
Note:
cid:verificationMethodpredicate on#meat all#nostr-key-1(noa Multikey, no controller, no publicKeyMultibase)cid:authentication <.../card.jsonld#nostr-key-1>survives (flat IRI)cid:service <.../card.jsonld#oidc>survives, AND the#oidcresource IS described (because it's referenced as a flat IRI from the subject AND its description is at the top level of the doc, not nested insideservice)So the rule seems to be: predicates whose values are inline objects (vs. flat IRI references with the resource described at the document root) get dropped.
Why this matters
src/auth/nostr.js/src/auth/cid-doc-fetch.js) requestsapplication/ld+json. The CID-VM check path is unaffected.cid:authentication <#nostr-key-1>but ZERO triples about#nostr-key-1. They can't find the key material — RDF-store integrations, SPARQL endpoints, CID validators that prefer Turtle, generic Solid clients that default to text/turtle.Likely root cause
src/rdf/turtle.js's emitter probably walks the JSON-LD object's top-level keys and emits a triple per predicate-value pair, but doesn't recursively descend into inline-object values to:@idCompare to how
serviceworks (and survives): the JSON-LD likely hasservice: [".../card.jsonld#oidc"]as a flat-IRI list AND a top-level#oidcblock describing the service endpoint. Two parallel descriptions. The Multikey VM is only declared as an inline object insideverificationMethod— there's no top-level#nostr-key-1block to fall back to.Possible fixes
A. Hoist + flatten on serialize: when emitting Turtle, replace inline objects with flat IRI references AND emit a top-level resource block per inline object. This is the standard JSON-LD → flat-N-Quads transformation.
B. Recursive emission: walk inline-object values, emit them as anonymous (or named, if
@idpresent) resource blocks alongside the parent.C. Switch to a real JSON-LD library:
jsonld.js(digitalbazaar) handles this correctly out of the box. JSS's deliberate choice to avoid remote-context fetches (#389) is preserved by passing a pre-resolved local context map.(C) is the most robust but biggest change. (A) or (B) would unblock the immediate use case.
Acceptance test
After the fix, the Turtle conversion of the test profile must contain:
(Whitespace + prefix-vs-IRI formatting may vary; the triple set must match.)
Refs
@type: '@json'literals not emitted — relevant when VMs usepublicKeyJwkinstead ofpublicKeyMultibase)