I have written a draft FEP and would like comment before opening the pull request.
The situation. Client-to-server submission is described for an object that is born with its activity: a client posts a Note, the server wraps it in a Create, and both are given identifiers. Where federation is added to a content store that already exists, the order is reversed, and that is the normal case for a content management system. The store assigns an identifier, an author and an audience when the content is written; a plugin or module adds federation later; so an object can sit there for years before any actor decides to publish it. Publishing older content, publishing selectively, and publishing under one of several actors are then ordinary requests.
For those servers the client has nothing new to submit. It wants to name something the server already hosts:
{ "@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "actor": "https://example.org/actors/alice", "object": "https://example.org/articles/123"}
The syntax is not new — this is the shape of the Minimal Activity example, with both properties as URIs. What is missing is what an outbox should do with it. Section 6.2.1 is written throughout for an object the server is about to create, including the requirement to attach an identifier to the wrapped object, and says nothing about one that already has an identifier. w3c/activitypub#438 asks a neighbouring question and has been open since 2024. MediaUpload reached for the same shape in 2017 and has not been edited since.
What the draft says. Most of it follows from roles the specifications already define: do not give the referenced object a new identifier; take the activity's addressing from the object rather than from the request; reject a request that carries its own to/cc/bto/bcc/audience rather than ignoring it, because ignoring it lets a client believe it published narrowly when it did not; refuse a reference the server cannot resolve rather than creating something to satisfy it; and check two permissions separately — permission to submit to that outbox, and permission for that actor to publish that object.
The part I think is new. Because such a submission carries no document, actor and object describe it completely, so a server can recognise a repeat as a retry. That is not generally possible in client-to-server: two submissions carrying identical documents may be two genuine posts. It does depend on the server's own policy — if a server lets the same actor publish the same object more than once, the reference alone does not distinguish a retry from a new publication — but where that policy exists, the submission is idempotent and the server records nothing the second time.
Which produces the one place the draft departs from a MUST. Section 6 requires 201 Created. When the server recorded nothing, 201 asserts a creation that did not happen, so the draft recommends 200 OK with the existing activity's Location. The same reasoning extends to a Delete naming an object by URI, with the complication that a deletion after an Undo(Delete) is a new transition rather than a retry.
What it deliberately leaves alone. How many Create activities one object may have, and who may publish them. A server may record one and treat its actor as the publishing authority; a server may accept one from each of several actors and call the object jointly published. Both should be able to implement this.
Questions.
- Is a referenced
Create a reasonable client-to-server submission at all, or should publishing an existing object stay out of band? - Is rejecting request-side addressing right, or does someone have a case for merging it?
- Is
200 on a recognised retry acceptable as a documented exception, or is there a better answer I have not found? - Has anyone hit this already and solved it differently? I would rather adopt an existing answer than add one.
We have this implemented in a WordPress plugin set and covered by tests; it is not in a released version yet. Draft text follows in the pull request once there is a discussion link to point at.