Skip to content

Cached item contexts can make collection pages invalid JSON-LD #1394

Description

@dahlia

A Like parsed from valid standalone JSON-LD can make an OrderedCollectionPage invalid when it is embedded in the page. The page's default toJsonLd() output retains the item's context but adds a parent context with protected terms that the item redefines. Parsing the resulting page fails with protected term redefinition.

This surfaced while serving saved activities in fedify-dev/drfed#177. It also occurs with ordinary Fedify vocabulary objects, without a custom toJsonLd() override or a database. In an outbox, one such item prevents a JSON-LD client from parsing the whole page, including its other activities.

Reproduction

Tested on Node.js 26.12.1, Linux x86_64, with the published @fedify/vocab 2.4.3 package. In an empty directory, run:

npm install @fedify/vocab@2.4.3

Save this as repro.mjs and run node repro.mjs:

import { Like, OrderedCollectionPage } from "@fedify/vocab";

const AS = "https://www.w3.org/ns/activitystreams";

for (const [name, context] of [
  ["control", AS],
  ["custom proof", [AS, { proof: "https://example.org/proof" }]],
]) {
  const like = await Like.fromJsonLd({
    "@context": context,
    id: "https://example.com/likes/1",
    type: "Like",
    object: "https://example.net/notes/1",
  });
  const page = await new OrderedCollectionPage({
    id: new URL("https://example.com/outbox?page=1"),
    items: [like],
  }).toJsonLd();

  try {
    await OrderedCollectionPage.fromJsonLd(page);
    console.log(`${name}: OK`);
  } catch (error) {
    console.log(`${name}: ${error.details?.code ?? error.message}`);
  }
}

Actual output:

control: OK
custom proof: protected term redefinition

Both standalone inputs pass Like.fromJsonLd(), and both page serializations complete. Only parsing the page containing the custom context fails. The proof property need not appear in the item; the context definition alone triggers the error.

Expected behavior

Both pages should remain valid JSON-LD and be readable through OrderedCollectionPage.fromJsonLd(). Embedding a parsed item should preserve its meaning without introducing a conflicting inherited context. A regression test should parse the serialized page, since checking the returned JSON or an HTTP status alone does not catch this.

Context composition and #1356

The default serialization path in packages/vocab-tools/src/codec.ts embeds each item's default toJsonLd() output and assigns the page's default context. That parent context includes https://w3id.org/security/data-integrity/v1, which protects proof. The cached child document retains its different definition of proof, so the combined document violates the protected-term rules in JSON-LD 1.1.

#1356 fixes a related cache problem: inherited parent contexts were being added to an embedded child's cached source, affecting proof verification. Its later decoder change avoids reparsing the child separately. Those changes preserve a child extracted from an existing document; this case instead combines an independent child with a new parent context.

The failure was also reproduced against #1356 at 935df52. Its existing embedded objects keep their cached JSON-LD as received regression test passes, while the reproduction above still fails. This report concerns context composition during serialization, not the raw-property read API.

Drafted and reproduced with assistance from Codex; prose reviewed with Claude Code.

Activity

  1. added theissue type on Nov 8, 2026
  2. self-assigned this
    on Nov 9, 2026
  3. added a commit that references this issue on Nov 9, 2026
  4. linked a pull request that will close this issueIsolate cached JSON-LD child contexts #1397on Nov 9, 2026
  5. modified the milestone: Fedify 2.5 on Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Fields

Priority

None yet

Effort

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions