Skip to content

Add FEP-ef61 compatible identifier conversion - #1074

Open
dahlia wants to merge 1 commit into
fedify-dev:mainfrom
dahlia:fep-ef61/identifiers
Open

dahlia wants to merge 1 commit into
fedify-dev:mainfrom
dahlia:fep-ef61/identifiers

Conversation

@dahlia

@dahlia dahlia commented Sep 26, 2026

Copy link
Copy Markdown
Member

Closes #833, part of #288.

Portable objects need HTTP(S) IDs for software that can't handle ap:/ap+ef61: URIs. This adds fromCompatibleEf61Id() and toCompatibleEf61Id() to @fedify/vocab-runtime so every later gateway feature converts the same way.

An HTTP(S) URL parser rewrites paths that portable IDs treat as opaque: it drops tabs, turns \ into /, and resolves dot segments. Pasting a portable path under /.well-known/apgateway/ could therefore name a different object. toCompatibleEf61Id() percent-encodes the path and fragment with canonicalizePortableUri()'s rules, rejects dot segments, and converts its output back to check that the canonical ID survived. Location hints are stripped by decoded name after the query is normalized, so a raw tab can't hide @gate<TAB>way until the parser deletes it; other parameters stay for collection paging. DIDs are written literally unless they contain %25, the one case the existing parser would decode.

fromCompatibleEf61Id() returns null for routes outside the DID object path, such as the discovery endpoint and hl: media URLs. It throws only for IDs that look compatible but are broken, including ones carrying location hints, which FEP-ef61 forbids.

canonicalizePortableUri(), arePortableUrisEqual(), and getFe34Origin() still treat compatible IDs as ordinary HTTP(S) URLs. Anyone can publish https://evil.example/.well-known/apgateway/did:key:VICTIM/…, so conversion stays explicit and proves nothing on its own; the proof still has to verify against the DID. Arbitrary gateway paths are out of scope. Fedify canonicalizes to ap+ef61: (#828), although the current FEP text now says ap:.

Software that does not understand ap:/ap+ef61: URIs can still refer to
portable objects through compatible identifiers: HTTP(S) URLs under a
gateway's fixed /.well-known/apgateway/ path.  The later FEP-ef61 work
(gateway dereferencing, portable inbox delivery, WebFinger, gateway
keys, and Context helpers) needs a single, well-tested conversion
between the two forms instead of hand-built strings.

This adds two helpers to @fedify/vocab-runtime:

- fromCompatibleEf61Id() turns a compatible identifier into the
  portable URI, returning null for URLs that are not compatible
  identifiers (including the discovery endpoint and hashlink media
  routes) and throwing a TypeError for malformed ones, such as those
  with an invalid DID, no object path, bad percent-encoding,
  credentials, or location hints.

- toCompatibleEf61Id() builds a compatible identifier from a portable
  URI and an HTTP(S) gateway origin.  It removes @gateway (and legacy
  gateways) location hints, which FEP-ef61 forbids in compatible
  identifiers, keeps other query parameters, and preserves the path
  and fragment.  Characters that an HTTP(S) URL parser would rewrite
  are percent-encoded the same way canonicalizePortableUri() does, and
  paths with dot segments are rejected, so the conversion never
  silently changes which object is identified.

Only the fixed well-known gateway path is supported.  The existing
comparison and origin helpers are unchanged; callers convert
compatible identifiers explicitly, and the docs point out that the
conversion does not authenticate anything: the object's proof still
has to be verified against its DID.

fedify-dev#833
fedify-dev#288

Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: OpenCode:deepseek-flash
Assisted-by: Codex:gpt-6-astra
Assisted-by: Claude Code:claude-fable-5-1
@netlify

netlify Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for fedify-json-schema canceled.

Name Link
🔨 Latest commit 5ca16ec
🔍 Latest deploy log https://app.netlify.com/projects/fedify-json-schema/deploys/6ab80fee2eee2d0008e6192d

@dahlia

dahlia commented Sep 26, 2026

Copy link
Copy Markdown
Member Author

@codex review

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c093ab25-88a3-452a-a9b7-89c017b56db3

📥 Commits

Reviewing files that changed from the base of the PR and between e6b3de3 and 5ca16ec.

📒 Files selected for processing (6)
  • CHANGES.md
  • changes.d/vocab-runtime/compatible-ef61-ids.md
  • docs/manual/vocab.md
  • packages/vocab-runtime/src/mod.ts
  • packages/vocab-runtime/src/url.test.ts
  • packages/vocab-runtime/src/url.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The runtime adds helpers to convert between FEP-ef61 portable identifiers and fixed-path HTTP(S) gateway identifiers. The changes also add validation tests, public exports, and documentation describing conversion behavior and proof verification.

Changes

FEP-ef61 compatible identifiers

Layer / File(s) Summary
Implement and validate identifier conversion
packages/vocab-runtime/src/url.ts, packages/vocab-runtime/src/mod.ts, packages/vocab-runtime/src/url.test.ts
Adds fromCompatibleEf61Id() and toCompatibleEf61Id(). The functions validate identifiers and gateway origins, handle location hints, and are covered by conversion and round-trip tests.
Document conversion behavior
docs/manual/vocab.md, changes.d/vocab-runtime/compatible-ef61-ids.md, CHANGES.md
Documents the conversion functions, supported gateway path, location-hint handling, error behavior, and the requirement to verify the object’s proof against its DID.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 5ca16

The new identifier conversions appear ready to merge after normal checks; no actionable issue was established.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 5ca16

The new converters validate identifier form but do not establish who controls a DID. The change documents that distinction, and no direct authorization change is shown. Applications using the converters must still verify object proofs.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Any HTTP(S) host can present a syntactically compatible URL claiming a DID, so downstream misuse could affect the identity that DID represents. The visible change supplies conversion, not fetching, proof verification, or authorization.

Trust Boundaries and Controls

  • observed — Syntax and canonical round-trip checks protect identifier representation; they are not DID authentication. The documented trust boundary leaves proof verification with the caller.

Hardening Proposals

  • proposed — When a downstream gateway or federation flow adopts these helpers, require proof verification against the claimed DID before assigning DID-origin trust, and retain the HTTP(S) URL separately when needed as a retrieval hint.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (3 skipped: 3… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding FEP-ef61 compatible identifier conversion.
Description check ✅ Passed The description directly explains the new conversion functions, supported identifiers, validation behavior, security considerations, and scope.
Linked Issues check ✅ Passed The PR implements the coding requirements in [#833]. fromCompatibleEf61Id() and toCompatibleEf61Id() are exported from @fedify/vocab-runtime. The implementation supports ap: and ap+ef61:, us…
Out of Scope Changes check ✅ Passed The changes remain within [#833]. The source changes implement fixed-path identifier conversion. The tests provide regression coverage. The exports, documentation, and changelog describe the new API a…
Full details: Docstring Coverage

Explanation

Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.60305% with 11 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/vocab-runtime/src/url.ts 91.47% 7 Missing and 4 partials ⚠️
Files with missing lines Coverage Δ
packages/vocab-runtime/src/mod.ts 100.00% <100.00%> (ø)
packages/vocab-runtime/src/url.ts 92.05% <91.47%> (-0.17%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

activitypub/compliance Specification compliance component/vocab Activity Vocabulary related

Development

Successfully merging this pull request may close these issues.

Compatible FEP-ef61 identifiers for portable objects

1 participant