Background ---------- [FEP-ef61] requires gateways listed by a portable actor to accept `POST` requests to the actor's inbox. For example, if a portable actor has this inbox: ~~~~ text ap+ef61://did:key:z6MkAlice/actor/inbox ~~~~ and one of its gateways is `https://server.example/`, the gateway-compatible inbox endpoint would be: ~~~~ text POST https://server.example/.well-known/apgateway/did:key:z6MkAlice/actor/inbox ~~~~ Fedify should support this endpoint without introducing a separate public inbox API for portable actors. Applications that already configure inbox dispatchers and inbox listeners should get portable inbox delivery automatically once the corresponding portable actor can be resolved and accepted by the local gateway. This issue is about routing gateway inbox requests into Fedify's existing inbox delivery pipeline. [FEP-ef61]: https://w3id.org/fep/ef61 Proposed work ------------- Add support in `@fedify/fedify` for receiving portable actor inbox deliveries through the fixed FEP-ef61 gateway endpoint. The work should include: - handling `POST /.well-known/apgateway/{did}/{path...}/inbox`; - reconstructing the canonical `ap+ef61:` inbox IRI from the DID authority and gateway path; - identifying the owning portable actor and resolving its `gateways` property; - deciding whether the local gateway accepts delivery for that actor; - returning `404 Not Found` when the local server does not accept delivery for the target portable actor; - reusing the existing Fedify inbox dispatcher and inbox listener pipeline instead of adding a new portable inbox API; - applying the FEP-ef61 proof policy to portable activities and objects in the delivered payload; - preserving existing ordinary ActivityPub inbox delivery behavior; - defining status-code behavior for malformed portable IDs, unknown actors, unsupported actors, and invalid proofs. Forwarding needs to be part of the design. When a gateway accepts an activity for a portable actor, Fedify should be able to forward that activity to the actor's other gateways so delivery can reach the other places where the actor's data is stored. Forwarding should use the actor's `gateways` list, skip the gateway that received the current request, and avoid sending the activity back to the gateway it came from when that can be identified. Fedify also needs a loop-prevention and idempotency policy for gateway inbox forwarding. FEP-ef61 says an activity must not be forwarded from an inbox more than once. The implementation should define how to mark or recognize a forwarded gateway delivery, how long to remember forwarded activity IDs, and how this interacts with the existing inbox idempotency behavior. This should avoid both forwarding loops and duplicate application-level delivery. Design questions ---------------- The implementation should settle these API and behavior details before finalizing the code path: - How should Fedify map the gateway path back to the existing actor/inbox dispatcher model? - Should accepting portable inbox delivery be implicit from serving the portable actor, or should there be an explicit opt-in? - Should `InboxContext` expose the portable target IRI or gateway request information? - Where should inbox-level idempotency and “forwarded once” tracking live? - What marker or request metadata should Fedify use to distinguish an original delivery from a gateway-forwarded delivery? - Should forwarded gateway deliveries use an HTTP header, a request signature property, local queue metadata, or some other mechanism to prevent re-forwarding? - Which activity identifier should be used for idempotency when the delivered activity has no stable `id`? - Which HTTP signature key should be used for gateway-to-gateway forwarding? - How should non-portable activities delivered to a portable actor inbox be handled? Scope ----- This issue is only about inbound portable inbox delivery through the fixed FEP-ef61 gateway endpoint. It does not include: - [FEP-ae97] outbox posting; - actor registration; - media upload or deletion; - gateway discovery; - object serving endpoints; - media serving endpoints; - automatic storage for portable inbox payloads. [FEP-ae97]: https://w3id.org/fep/ae97 Dependencies ------------ This should depend on the lower-level portable URI, vocabulary, proof policy, and compatible identifier work under #288: - #826 for `ap:`/`ap+ef61:` URI support in vocabulary codecs; - #828 for portable URI canonicalization and comparison; - #829 for FEP-fe34 cryptographic origins; - #830 for FEP-ef61 vocabulary terms, including `gateways`; - #832 for the FEP-ef61 proof policy; - #833 for compatible FEP-ef61 identifier conversion. It may also share gateway request handling code with #835, but portable inbox delivery should not require the object-serving endpoint to be implemented first. Tests ----- Add regression tests for portable inbox delivery. The tests should cover: - routing `POST /.well-known/apgateway/did:key:.../actor/inbox` into the existing inbox delivery pipeline; - reconstructing the expected `ap+ef61:` inbox IRI; - accepting delivery when the local server is an allowed gateway for the target portable actor; - returning `404 Not Found` when the local server does not accept delivery for the target portable actor; - rejecting malformed DID/path requests; - applying the FEP-ef61 proof policy to portable activities and objects; - preserving existing ordinary ActivityPub inbox behavior; - forwarding an accepted activity to other actor gateways when appropriate; - skipping the current gateway and, where possible, the gateway that sent the forwarded delivery; - ensuring an activity is not forwarded from an inbox more than once; - avoiding duplicate listener execution for duplicate gateway deliveries. This should be added as a sub-issue of #288.