FEP-400e: Publicly-appendable ActivityPub collections

Indeed it appears that it does.

  • If the object could not be added to the collection, for example due to the privacy settings configured by its owner, the server SHOULD either respond with 403 Unauthorized or respond with 200 OK and later send a Reject{Create} activity to the originating server.

Okay here are my comments on the FEP:

Publicly-appendable collections:

either a Collection or an OrderedCollection object that contains the links to all its objects

Why does it have to contain links to the objects? Thats quite inefficient, as you will have to fetch each object one by one. Its more efficient to have objects directly embedded in the collection json. So this should probably be left for each implementation to decide.

Specifying collections in actors

So it would look something like this? Its a good idea. Would be good to include an example in the FEP.

"wall": "https://friends.grishka.me/grishka/wall",
"albums": "https://friends.grishka.me/grishka",

Using target in objects

Why is this SHOULD and not MUST? If the target field is not set, how do other instances know which collection it belongs to?

And I dont really see why its necessary to include the type or attributedTo fields. In Lemmy we would just do a db query with the collection id to get that other info, so it doesnt really simplify anything. Maybe its useful for Smithereen, shouldnt be part of a standard that many different projects will use.

And the target field is also required in activities, right? Sure in case of a Create/Note you can get the collection id from the Note, but there are many activities in Lemmy which dont have an object (eg Like). This is mentioned in the “discussion”, but seems important enough to make a general section “target field”, which applies to both activities and objects in a collection.

Adding an object to a collection

This seems more like an example than part of the spec. I would rename it as such, and move it to the end, also with some example json as mentioned above.

One thing I would definitely like you to add is a description of the HTTP signature based activity forwarding that you mentioned in the other thread. I know HTTP sigs are not part of the Activitypub standard, but this seems like the best place to document it. And so far i’m not really sure how it works.

Lemmy also has many other actions, but all of them work in the same way (user sends to community, community announces to followers). So you could describe the forwarding in a more generic way (with the http signatures), and have the part about deleting as another example.

1 Like

Because you most probably don’t store the original json, and also for security reasons so the origin server retains its part of authority over the object. You can embed your own objects I think? But the ones from other servers have to be links. Mastodon does this with e.g. replies.


So you could avoid having a zillion indexes in your database, and also could use in-memory cache, and so on. It’s much easier to first query the actor and then find which collection of that actor it is than to have this many different ways to reference it. Also, imagine an actor having 5 different collections, which field do you query on then? Or do you have to have a separate table that maps collection IDs to actors? It does say “simplify database design” for a reason :wink:

No, why? And why would you want that in Like anyway?

I don’t support it (yet?) in Smithereen, but the basic idea is that you would forward an activity, re-signing it with whatever actor you want, and the receiving server would re-fetch it from the origin. This obviously requires the activity to have a unique, resolvable id.

Okay the security aspect makes sense, I think you should mention that in the FEP.

Dont know what you’re trying to say with this.

I guess simplifying database design is not one of our goals. And we dont use in-memory cache either. In our case there is exactly one collection per actor, so its already pretty simple in that regard.

It simplifies the logic a lot if you can simply check the target field to determine if an actor is allowed to post in the community (it might have been banned). Otherwise you would have to resolve the object first, or even resolve it recursively (eg in case of Undo/Like/Note).

That maybe I meant “MUST” there.

In your case yes. But in my case there will be photo albums, and discussion boards, and who knows what else, in addition to walls. And if I’m to copy VK in this regard, a group could be configured so anyone could create a photo album in it. And each photo album could be configured so anyone could upload new photos to it.

But you’ll have to anyway, sooner or later? To check if it exists, if there’s already a like from that user on it, and so on.

IIRC I only support Undo{Like{Note}} if the inner Like is an object, not a link.

1 Like

Well if your goal for this FEP is to describe Smithereen behaviour, then it is probably okay in the current form. But for Lemmy it doesnt really make sense to implement things in that specific way.

Not if the actor is blocked in the community, and cant post there. Or in case its a private community. Of course if the user is allowed to post, i will have to dereference everything and verify that the target community matches the object.

This “authority” of the “origin” server sounds out of place in a decentralized context. It seems to me that it’s another case of “please delete” and non-compliant servers will simply ignore this. Can you please expand on what security reasons you see here?

There are two issues I’m trying to solve:

  • The group needs to be able to moderate its content.
  • The user needs to be sure that they are not impersonated and the content they contribute to the group isn’t tampered with.

What I’m proposing (and what is already implemented in Smithereen) is essentially two-way linking, which is ubiquitous on the web when you want to somehow link two services together or prove the ownership of something. The group links to the post from its wall collection, and the post links to the collection from its target field. If any one side removes its link, the entire relationship is invalidated. So no, it’s not “please delete”.

  • If the group deletes the post but the user does not honor this request, the link to the post is no longer in the group’s collection anyway (and it sends a Delete{Note} activity to all followers). Other servers will notice that and consider that post invalid.
  • If the user is blocked in the group but tries posting in it anyway, the group won’t accept that activity and won’t add the link to that post to its collection in the first place.
  • If the user deletes the post but the group keeps the link, this link is no longer valid because it won’t resolve any more.

But if you make groups return objects themselves instead of links, that would break the user’s ability to delete or edit the posts, and it would also enable the group to impersonate the user because the post isn’t signed by that user’s key and doesn’t come from their server. Even disregarding that (though you really shouldn’t be disregarding that), if the user’s and group’s servers run different software, they might mangle each other’s objects because to my knowledge no one ever stores the original JSON.

So this leaves us with this model of authority:

  • The group is authoritative for the inclusion of an object in its collection.
  • The user is authoritative for the object itself.

We already have this in the microblogging model most everyone implements. For an activity or object to be considered authoritative, it either needs to be signed with the key of its creator upon receipt, or come from their server when fetching via id. I’m just building on top of that by adding the ability to split this authority across two servers.

1 Like

I’ve requested the finalization of this FEP and I ask for any final comments before that happens.


Hi @grishka, thank you for this FEP. I’ve been working on FEP-8485 that proposes a modelling of an “unbound actor” and uses this to achieve it.

If I understood your FEP correctly, it is aimed at public collections only. That isn’t always the case of the collection described in FEP-8485, and thus I suggested handling the case in which it is public as you describe, and the case in which it isn’t by using the type Offer instead of Create. Can you have a look at it and share your thoughts?

It’s not necessarily public (though I don’t really believe in true privacy in federated systems). The existence of the collection must be public, but its owning server is free to control access to it however it sees fit. For example, requiring that it must be fetched with a GET request signed by a pre-approved actor. The objects themselves could use bearcaps (a bear: URI that includes an access token and the actual URL of the object, @dansup uses these in pixelfed for stories iirc), or maybe just some kind of random URLs that are infeasible to enumerate.

I’ll read your FEP later.

Yeah I don’t see any issues with private collections here

However, I still disagree with the idea that such a collection has any authority over the objects themselves, and will not be implementing it as described

That’s how walls commonly work — it’s part of your profile and thus you can delete anything and everything anyone has posted on there. I just replicated this pattern in a federated manner.

Well. It’s specifically designed to work in case one side doesn’t cooperate as two-way linking is required for an object to belong to a collection. If the object doesn’t link to the collection, it’s considered to be standalone (i.e. posted to the timeline/profile). If the collection doesn’t link to the object, it’s considered to be invalid because the collection owner didn’t approve its inclusion into the collection.

The way I interpret this is that having target set without inclusion in the collection is an expected case, since that’s the way it will be before the collection owner accepts the object. Removing the object from the collection would therefore simply return it to this “unapproved” state

I used the term public wrongly. I meant to say that anyone could add things to collections. But you have just explained that it’s possible to have pre-approved actors. The usage I suggested in FEP-8485 was: when it is subject to approval, then Offer the object to the collection, so the owner of the collection answers either with an Accept or Reject. Otherwise, if the collection is “open”, as in anyone can add objects to it, (this is why I wrote “public” earlier), then use Create.

Having the target property in the object is a spec violation (because of the domain of the target property). It is a property of Activity and not of Object. But I understand the usefulness of knowing that “an object was created to be part of a collection and should only be considered in its context”. Therefore, maybe for objects use the context property instead and keep using target only in activities?

For example:

  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "http://example.org/activity/123",
  "summary": "Sally posted a note to John's wall.",
  "type": "Create",
  "actor": "acct:sally@example.org",
  "object": {
    "id": "http://example.org/notes/123",
    "type": "Note",
    "content": "hello, world.",
    "context": {
      "target": "http://example.org/john/wall"
  "target": "http://example.org/john/wall"

On another note, how would we use this to add activities to a collection? That would make the use of target problematic since it has another meaning there

I’m not sure what the current uses of context are and whether that would create conflicts (pleroma seems to be setting it), maybe it would be better to add a new field

You wouldn’t add activities to a collection. This is specifically for objects. I treat activities as more or less transient things. Some of mine (like Create{Note}) do have persistent URLs and I’m able to recreate them, but many do not.

Activity is a subtype of Object, so they should be able to do anything other objects can. Specifically Question is often treated like a Note


As this FEP was just finalized, it becomes the second and really the first non-meta FEP. Congratulations @grishka!