Skip to content

Canonicalize and compare ap:/ap+ef61: URIs #828

Description

@dahlia

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.

Metadata

Metadata

Assignees

Fields

Priority

High

Effort

Medium

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions