Background
FEP-ef61 defines comparison rules for portable ActivityPub object identifiers. Two ap URIs are considered equivalent when their canonical forms are identical.
The spec currently defines canonicalization for ap: URIs as:
- convert a compatible identifier into an
ap URI;
- percent-decode the authority component if needed;
- remove the query component.
Fedify also needs to account for ap+ef61: because the FEP warns that the URI scheme may change from ap to ap+ef61. We plan to treat ap+ef61: as the canonical portable-object scheme while still accepting ap: input for compatibility with the current FEP text.
This comparison layer is separate from the vocabulary codec work. The codecs need to preserve portable URIs during parse/serialization, while this issue is about deciding when two portable URI values identify the same portable object.
Proposed work
Add portable URI canonicalization and comparison helpers to @fedify/vocab-runtime.
The helpers should:
- accept both
ap: and ap+ef61: URI values;
- accept both decoded DID authority forms, such as
ap+ef61://did:key:.../actor, and URL-safe encoded forms, such as ap+ef61://did%3Akey%3A.../actor;
- normalize
ap: input to ap+ef61: in canonical output;
- percent-decode the DID authority component in canonical output;
- remove the query component for comparison;
- preserve the path and fragment components according to URI semantics;
- provide an equality helper, such as
arePortableUrisEqual(), that returns whether two portable URI values have the same canonical form.
For example, these should compare as equivalent:
ap://did:key:z6Mkabc/actor
ap://did%3Akey%3Az6Mkabc/actor
ap://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fa.example
ap+ef61://did:key:z6Mkabc/actor
ap+ef61://did%3Akey%3Az6Mkabc/actor?gateways=https%3A%2F%2Fa.example
Their canonical comparison form should be:
ap+ef61://did:key:z6Mkabc/actor
The helper names can be decided during implementation, but the intended API is roughly:
canonicalizePortableUri(input: string | URL): string;
arePortableUrisEqual(left: string | URL, right: string | URL): boolean;
Scope
This issue is only about canonicalizing and comparing ap:/ap+ef61: URI values.
It does not include:
- compatible HTTP identifier conversion, such as
https://server.example/.well-known/apgateway/did:key:.../actor to ap+ef61://did:key:.../actor;
- gateway dereferencing;
- DID document resolution;
- FEP-8b32 proof verification;
- FEP-fe34 cryptographic origin checks.
Compatible HTTP identifier conversion should be handled in a separate follow-up issue, because it depends on gateway base URL and path handling.
Tests
Add regression tests covering canonicalization and comparison.
The tests should cover:
- decoded
ap: input;
- encoded
ap: input;
- decoded
ap+ef61: input;
- encoded
ap+ef61: input;
- query stripping, including
gateways query hints;
- path preservation;
- fragment preservation;
- non-equivalence when DID authorities differ;
- non-equivalence when paths differ;
- rejection of unsupported schemes.
Background
FEP-ef61 defines comparison rules for portable ActivityPub object identifiers. Two
apURIs are considered equivalent when their canonical forms are identical.The spec currently defines canonicalization for
ap:URIs as:apURI;Fedify also needs to account for
ap+ef61:because the FEP warns that the URI scheme may change fromaptoap+ef61. We plan to treatap+ef61:as the canonical portable-object scheme while still acceptingap:input for compatibility with the current FEP text.This comparison layer is separate from the vocabulary codec work. The codecs need to preserve portable URIs during parse/serialization, while this issue is about deciding when two portable URI values identify the same portable object.
Proposed work
Add portable URI canonicalization and comparison helpers to
@fedify/vocab-runtime.The helpers should:
ap:andap+ef61:URI values;ap+ef61://did:key:.../actor, and URL-safe encoded forms, such asap+ef61://did%3Akey%3A.../actor;ap:input toap+ef61:in canonical output;arePortableUrisEqual(), that returns whether two portable URI values have the same canonical form.For example, these should compare as equivalent:
Their canonical comparison form should be:
The helper names can be decided during implementation, but the intended API is roughly:
Scope
This issue is only about canonicalizing and comparing
ap:/ap+ef61:URI values.It does not include:
https://server.example/.well-known/apgateway/did:key:.../actortoap+ef61://did:key:.../actor;Compatible HTTP identifier conversion should be handled in a separate follow-up issue, because it depends on gateway base URL and path handling.
Tests
Add regression tests covering canonicalization and comparison.
The tests should cover:
ap:input;ap:input;ap+ef61:input;ap+ef61:input;gatewaysquery hints;