Skip to content

Serve portable objects through existing object dispatchers #835

Description

@dahlia

Background

FEP-ef61 defines a gateway endpoint for retrieving portable ActivityPub objects over HTTP. A gateway serves objects identified by ap: URIs through a well-known path such as:

GET /.well-known/apgateway/did:key:z6MkAlice/users/alice/notes/123

Fedify already serves dereferenceable ActivityPub objects through object dispatchers registered with setObjectDispatcher(). An object dispatcher maps a vocabulary class and a URI Template path, such as /users/{userId}/notes/{noteId}, to application code that returns the object.

For the first FEP-ef61 gateway serving implementation, Fedify should reuse that same object dispatcher path as the portable object path. In other words, if an application has this dispatcher:

federation.setObjectDispatcher(
  Note,
  "/users/{userId}/notes/{noteId}",
  async (ctx, { userId, noteId }) => {
    // ...
  },
);

then this portable gateway request should route to the same dispatcher:

GET /.well-known/apgateway/did:key:z6MkAlice/users/alice/notes/123

The requested portable object ID would be reconstructed as:

ap+ef61://did:key:z6MkAlice/users/alice/notes/123

This keeps the first implementation close to Fedify's existing routing model instead of introducing a separate portable object dispatcher API.

Proposed work

Extend the existing object dispatcher machinery so Fedify can serve portable objects through the fixed FEP-ef61 gateway endpoint.

The work should include:

  • handling GET /.well-known/apgateway/{did}/{path...};
  • reconstructing the requested portable ID as an ap+ef61: URI from the DID authority and dispatcher path;
  • routing the portable path portion through the existing object dispatcher router;
  • using the same URI Template path registered through setObjectDispatcher() as the portable object path;
  • adding or extending context helpers so object dispatchers can construct portable object IDs, similar to Context.getObjectUri();
  • giving object dispatchers enough request context to know whether they are serving an ordinary HTTP object URL or a portable gateway request;
  • validating that the returned object's ID canonically matches the requested portable ID;
  • applying FEP-ef61 proof policy before serving portable actors, activities, and non-collection objects;
  • returning ActivityPub JSON-LD with the required content type;
  • returning 404 Not Found when no object dispatcher matches or the dispatcher returns null;
  • preserving existing object dispatcher behavior for ordinary HTTP object requests.

The exact API can be decided during implementation, but a possible context helper could look like:

ctx.getPortableObjectUri(Note, {
  userId: "alice",
  noteId: "123",
});

The helper would use the current portable DID authority when called during a gateway request, or require an explicit DID authority when called outside a gateway request.

Design questions

The implementation should settle these API details before finalizing the code path:

  • Should portable serving be enabled automatically for every object dispatcher, or should applications opt in per dispatcher?
  • How should a dispatcher distinguish an ordinary HTTP request from a portable gateway request?
  • Should ctx.getPortableObjectUri() require an explicit DID authority outside a gateway request?
  • Should dispatcher authorization predicates run unchanged for portable gateway requests, or should they receive additional portable-request context?
  • How should private object access and signed GET behavior fit into the existing object authorization model?

Scope

This issue is only about read-only serving of local portable objects through the fixed well-known FEP-ef61 gateway endpoint using existing object dispatchers.

It does not include:

  • GET /.well-known/apgateway discovery metadata;
  • gateway dereferencing of remote portable objects;
  • inbox or outbox POST handling;
  • gateway-to-gateway forwarding;
  • FEP-ae97 actor registration;
  • media upload, serving, or deletion;
  • arbitrary gateway path support;
  • automatic storage for portable objects.

Private object access and signed GET behavior should be considered during design. If it requires broader authorization API changes, it can be split into a follow-up issue.

Dependencies

This should depend on the lower-level portable URI and proof policy work under #288, especially:

  • portable URI codec support;
  • portable URI canonicalization and comparison;
  • FEP-fe34 cryptographic origins;
  • FEP-ef61 proof policy.

Tests

Add regression tests for portable gateway object serving through object dispatchers.

The tests should cover:

  • routing GET /.well-known/apgateway/did:key:.../users/alice/notes/123 to an existing setObjectDispatcher(Note, "/users/{userId}/notes/{noteId}", ...) dispatcher;
  • reconstructing the expected ap+ef61: portable ID;
  • constructing a portable object ID from context inside a gateway request;
  • returning 404 Not Found when no object dispatcher matches;
  • returning 404 Not Found when the matched dispatcher returns null;
  • returning ActivityPub JSON-LD with the expected Content-Type;
  • rejecting or failing a dispatcher result whose ID does not canonically match the requested portable ID;
  • applying FEP-ef61 proof policy before serving portable actors, activities, and objects;
  • preserving existing ordinary object dispatcher behavior;
  • rejecting malformed DID/path requests.

This should be added as a sub-issue of #288.

Activity

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

Metadata

Metadata

Assignees

Fields

Priority

High

Effort

High

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions