Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .agents/skills/add-vocab/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ Embedded context entries are YAML mappings where:
- String value `"prefix:term"` or `"https://..."` defines a simple term alias
- Object value with `"@id"` and optionally `"@type": "@id"` defines a term
that should be treated as an IRI (linked resource)
- Object value may also carry `"@container": "@set"` or
`"@container": "@list"` to match a vocabulary whose context defines a
container. A context term's `@container` is separate from the
property-level `container: list` option, which controls how the compact
serializer writes list values.

### Ensuring complete compaction coverage

Expand Down
25 changes: 25 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ To be released.
for maps that will be rejected. Policy-rejected maps now appear in
`onRequestFinished` verification reports with their policy failure and no
signature checks. [[#1177], [#1264]]
- Changed compound proof verification to accept `"@container": "@set"` term
definitions in actor contexts when deciding whether embedded portable keys
are covered by the actor's proof. A `@set` container only forces expanded
values into arrays, so it does not make key expansion ambiguous; `@list`
and other containers remain rejected. This keeps portable actors that
publish container terms such as Mastodon's `attributionDomains`
verifiable. [[#1234], [#1353]]
- Fixed log entries emitted during an individual outbound activity delivery
or inbound activity processing carrying the enclosing HTTP request's or
queue worker's `traceId`/`spanId` instead of the delivery or processing
Expand All @@ -75,12 +82,14 @@ To be released.
[#1030]: https://github.com/fedify-dev/fedify/issues/1030
[#1177]: https://github.com/fedify-dev/fedify/issues/1177
[#1205]: https://github.com/fedify-dev/fedify/pull/1205
[#1234]: https://github.com/fedify-dev/fedify/issues/1234
[#1264]: https://github.com/fedify-dev/fedify/pull/1264
[#1289]: https://github.com/fedify-dev/fedify/issues/1289
[#1290]: https://github.com/fedify-dev/fedify/issues/1290
[#1299]: https://github.com/fedify-dev/fedify/pull/1299
[#1318]: https://github.com/fedify-dev/fedify/issues/1318
[#1328]: https://github.com/fedify-dev/fedify/pull/1328
[#1353]: https://github.com/fedify-dev/fedify/pull/1353

### @fedify/cli

Expand Down Expand Up @@ -287,6 +296,17 @@ To be released.
entries may not be visited. Accessor and serialization traversal costs are
unchanged. [[#1243], [#1337]]

- Added the `attributionDomains` property to `Application`, `Group`,
`Organization`, `Person`, and `Service`, so applications can publish the
domains an actor claims for Mastodon's `fediverse:creator` link credits
without subclassing the actor types. Each value is a bare host name
without a scheme, port, path, or leading `*.`; Fedify treats the values as
opaque strings and does not normalize them. Actor serialization and
`Update` documents that embed an actor now compact the property as
`attributionDomains` instead of `toot:attributionDomains`, which is the key
Mastodon reads when it refreshes an account, and parsing accepts both
a single string value and an array of strings. [[#1234], [#1353]]

- Added vocabulary support for the [FEP-6757] draft, which marks
ActivityPub content with explicit copyright license URIs.
[[#1212], [#1241]]
Expand Down Expand Up @@ -327,6 +347,11 @@ To be released.

### @fedify/vocab-tools

- Added `@container` support to embedded `defaultContext` term definitions
in the vocabulary schema. Terms such as Mastodon's `attributionDomains`
can now use `"@container": "@set"` or `"@container": "@list"` to match
their published JSON-LD contexts, while existing term definitions keep
validating as before. [[#1234], [#1353]]
- Added non-functional `redundantProperties` support with ordered fallback
to the first non-empty value set, preserving lists and graphs without
merging synonyms. Added `redundantPropertiesWrite: canonical` to write
Expand Down
12 changes: 12 additions & 0 deletions changes.d/fedify/compound-proof-set-contexts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
links:
'#1234': https://github.com/fedify-dev/fedify/issues/1234
'#1353': https://github.com/fedify-dev/fedify/pull/1353
---
- Changed compound proof verification to accept `"@container": "@set"` term
definitions in actor contexts when deciding whether embedded portable keys
are covered by the actor's proof. A `@set` container only forces expanded
values into arrays, so it does not make key expansion ambiguous; `@list`
and other containers remain rejected. This keeps portable actors that
publish container terms such as Mastodon's `attributionDomains`
verifiable. [[#1234], [#1353]]
10 changes: 10 additions & 0 deletions changes.d/vocab-tools/context-container-terms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
links:
'#1234': https://github.com/fedify-dev/fedify/issues/1234
'#1353': https://github.com/fedify-dev/fedify/pull/1353
---
- Added `@container` support to embedded `defaultContext` term definitions
in the vocabulary schema. Terms such as Mastodon's `attributionDomains`
can now use `"@container": "@set"` or `"@container": "@list"` to match
their published JSON-LD contexts, while existing term definitions keep
validating as before. [[#1234], [#1353]]
15 changes: 15 additions & 0 deletions changes.d/vocab/toot-attribution-domains.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
links:
'#1234': https://github.com/fedify-dev/fedify/issues/1234
'#1353': https://github.com/fedify-dev/fedify/pull/1353
---
- Added the `attributionDomains` property to `Application`, `Group`,
`Organization`, `Person`, and `Service`, so applications can publish the
domains an actor claims for Mastodon's `fediverse:creator` link credits
without subclassing the actor types. Each value is a bare host name
without a scheme, port, path, or leading `*.`; Fedify treats the values as
opaque strings and does not normalize them. Actor serialization and
`Update` documents that embed an actor now compact the property as
`attributionDomains` instead of `toot:attributionDomains`, which is the key
Mastodon reads when it refreshes an account, and parsing accepts both
a single string value and an array of strings. [[#1234], [#1353]]
4 changes: 3 additions & 1 deletion claude-plugin/skills/actor/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Walk through:
object or `FederationBuilder`, including the `{identifier}` path
parameter.
2. Returning a `Person`, `Service`, or other `Actor` vocabulary object
with the required fields (`id`, `inbox`, `publicKey`, etc.).
with the required fields (`id`, `inbox`, `publicKey`, etc.). Set
`attributionDomains` to bare host names when the actor should be
credited on Mastodon link previews; Fedify does not normalize them.
3. Chaining `.setKeyPairsDispatcher()` to supply RSA and Ed25519 keys.
4. Optionally configuring `mapActorAlias()` for handle-based or
fixed-path aliases.
Expand Down
8 changes: 8 additions & 0 deletions packages/fedify/src/federation/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ test("handleActor()", async () => {
"@type": "@id",
},
discoverable: "toot:discoverable",
attributionDomains: {
"@id": "toot:attributionDomains",
"@container": "@set",
},
indexable: "toot:indexable",
memorial: "toot:memorial",
suspended: "toot:suspended",
Expand Down Expand Up @@ -326,6 +330,10 @@ test("handleActor()", async () => {
"@type": "@id",
},
discoverable: "toot:discoverable",
attributionDomains: {
"@id": "toot:attributionDomains",
"@container": "@set",
},
indexable: "toot:indexable",
memorial: "toot:memorial",
suspended: "toot:suspended",
Expand Down
34 changes: 34 additions & 0 deletions packages/fedify/src/sig/compound-proof-verification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,28 @@ test("verifyCompoundPortableObjectProofs() exempts keys embedded in portable act
assert(result.status === "ok");
assert(result.verified);
assertEquals(result.portableObjects.map((o) => o.path), [""]);
// A parent context may carry a "@set" container term without making the
// keys ambiguous:
const actorWithSetTerm = await compatibleActor();
result = await verifyCompoundPortableObjectProofs(
await signCompatible({
...actorWithSetTerm,
"@context": [
...actorWithSetTerm["@context"] as unknown[],
{
tags: {
"@id": "https://example.com/tags",
"@container": "@set",
},
},
],
}),
limits,
options,
);
assert(result.status === "ok");
assert(result.verified);
assertEquals(result.portableObjects.map((o) => o.path), [""]);
// An ap: actor with gateway keys at compatible identifiers, as Fedify's
// own portable actors have:
result = await verifyCompoundPortableObjectProofs(
Expand Down Expand Up @@ -863,6 +885,18 @@ test("verifyCompoundPortableObjectProofs() exempts only keys of the parent itsel
{ kind: "@type" },
],
},
"a parent context with a list container": {
...actor,
"@context": [
...actor["@context"] as unknown[],
{
tags: {
"@id": "https://example.com/tags",
"@container": "@list",
},
},
],
},
"a key with a scoped context": {
...actor,
publicKey: {
Expand Down
10 changes: 9 additions & 1 deletion packages/fedify/src/sig/compound-proof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,8 @@ function isKeyIdOf(id: string, portableId: string): boolean {
* Checks whether a `@context` value defines no keyword aliases and no scoped
* contexts: only references to contexts and term definitions that map terms
* to IRIs, optionally with an `@id` or `@vocab` type coercion or a datatype.
* A `@set` container is accepted because it only forces expanded values into
* arrays, which expansion does anyway; `@list` and other containers are not.
*/
function isSimpleContext(context: CompoundProofJsonValue | undefined): boolean {
if (typeof context === "string") return true;
Expand All @@ -1330,9 +1332,15 @@ function isSimpleContext(context: CompoundProofJsonValue | undefined): boolean {
if (term.startsWith("@")) return false;
if (typeof definition === "string") return !definition.startsWith("@");
if (!isCompoundProofJsonObject(definition)) return false;
const { "@id": id, "@type": type, ...rest } = definition;
const {
"@id": id,
"@type": type,
"@container": container,
...rest
} = definition;
return Object.keys(rest).length < 1 &&
typeof id === "string" && !id.startsWith("@") &&
(container === undefined || container === "@set") &&
(type === undefined || type === "@id" || type === "@vocab" ||
typeof type === "string" && !type.startsWith("@"));
});
Expand Down
Loading
Loading