Groups implementation

As you agreed that Group IS an Actor, why you post not the Group itself (putting its “id” in the “to” or “cc” field), exactly like you post to any other Actor?!
Please see Example 15 here: - the Activity is sent to two groups. Their IDs (not outboxes!) are put into “cc”

I probably conflated the URL where the activity is delivered with the addressee that goes in the to field.

  1. The client would send a message to the group’s actor.
  2. The client’s server would look up the inbox of the group actor and deliver the message there (I mistakenly said outbox above).
  3. The group’s server would then relay the message to the inbox of every actor of the group. This message would have the group as the actor and the original client as the attributedTo.
  4. Recipient’s clients can now organize the message as they see fit, since they can tell both the group from which the message came, and the actor to which it is affiliated.

This has the benefit of not allowing group members the privilege of sending messages directly to other group members. The same pattern would apply to likes, et al.

Again, to me groups differ from collections in that groups are actors with an inbox that can accept activities and relay them. Collections e.g., followers are sets of actors to which activities are sent by the actors that can see them.

As you understood already, I’m looking at your solution from a User’s perspective, imagining what I, as the Group member, will receive from your implementation.
And I see that instead of posts, created by people whom I want to hear firsthand, I will receive posts from some middleman-robot. All group posts will be from that robot… So on arrival of activities from your robot I will have to identify them somehow and reconstruct original post: move the technical “Actor” from the “actor” position to a recipient, replace it with the original author… and hope that your robot didn’t change other properties much… (there are other properties in the original Activity…).
But having this reconstructed activity I cannot even tell how far it is from the original. I wouldn’t even have ID of the original activity and of the Note in it, and the Note’s deduplication will become a problem…

And why will I have to do all that?
The security-related benefit that you mentioned doesn’t count, because we didn’t agree with you, the Developer of that robot, about this custom way to protect me (this is completely another story, and related solutions don’t change original documents AT ALL…)


Really, rereading spec now I see that I missed that “Collections” part and modelled in my app Public, Followers and Following (friends) as special groups, types of Actors. Maybe there is no much difference inside a Client app, but your words explained to me, why I cannot find “Followers group” object on a server…

Yeah, Groups are Actors, Collections are not Actors. Both can be addressees on an Object, but Collections cannot receive activities because they do not have an inbox; the client’s server must (AFAICT) expand the collection to its members and deliver the activity to those members. The group’s server must expand messages sent to the Group’s inbox.

This means that if a client sends a Like activity to a Note of a Person with 50,000 followers, the client’s server is on the hook for sending 50,000 messages.

If a member of a Group with 50,000 members sends a Like activity to a Group for a post sent by a member of that group, the client’s server sends a single like to the Group’s inbox, and the Group’s server expands that message to all the group’s members.

But that’s just my interpretation; I could be wrong.

I’ve written a server that takes C2S Objects, creates activities, and sends them to whatever addressees are on the activity. It manages collection expansion, resolving addressees down to actors down to their inboxes, and sends the activity to them. I use a three-stage, asynchronous queued process to manage this, to get activities delivered to recipients as quickly as possible, in case one of the addressees’ servers is offline, or an addressee is a Collection with 50,000 members.

1 Like

I think that the Group’s server should minimize its work by forwarding the incoming Activity not to each Actor - member of the Group, but to each Server’s “shared inbox”, e.g. by putting all members from one server to the “bto” field (used for delivery only and removed by the target server), see

I think that Actors are confused with Users here. Actors cannot “implement C2S” ?!
These are Users, who need to have C2S Client app in order to have all features… for their Actors. See
“In ActivityPub, a user is represented by “actors” via the user’s accounts on servers. User’s accounts on different servers correspond to different actors. Every Actor has…”

The ActivityPub spec actually comes with a built-in solution to this problem, which is kind of ingenious: any collections owned by the receiving server are the responsibility of the receiving server to dereference and forward, subject to some restrictions.

This covered in the Inbox Forwarding section, and it handles both the ghost reply problem and other types of “forwarding” situations:

When Activities are received in the inbox, the server needs to forward these to recipients that the origin was unable to deliver them to. To do this, the server MUST target and deliver to the values of to , cc , and/or audience if and only if all of the following are true:

  • This is the first time the server has seen this Activity.
  • The values of to , cc , and/or audience contain a Collection owned by the server.
  • The values of inReplyTo , object , target and/or tag are objects owned by the server. [… (there’s some language about recursing through values here to find, e.g. inReplyTo grandparents that you may own) … ]

The server MAY filter its delivery targets according to implementation-specific rules (for example, spam filtering).

This might be helpful for solving your groups with 50,000 members problem!

And thinking further I see that both “Follow a Group” and “Join a Group” activities really make sense in terms of ActivityPub, but they have different meaning:

  • Join a Group - an intent to receive all activities sent to that Group by members of the Group.
  • Follow a Group - an intent to receive all activities sent by the Group i.e. by the Group as an Actor. This is similar to following a Server’s admin.


Could you explain your understanding of these words in details for this concrete case:
A “Create” Activity having a Group in its “to” property arrived to a Server, which hosts (owns) this Group. The Actor of this activity is a member of this group. The group has 500 members, some of them are from the same server, some from 4 others (to be concrete).
What should the Server (System) do?

Just a FYI that Solid is also doing stuff with Groups (which happens to include an AS Announce in it): Solid Groups App by @aveltens .

Personally I’m interested in taking Groups to a high level of functionality, with ability to nest them, associate them, have them be topical, and with moderation, governance and other types of Policies attached to them. This for 2 federated projects I’m investigating… early stages.

A very good point, and allows more flexibility (e.g. the followers only receive filtered information, like news, decision/poll outcomes, summaries, etc.). I would prefer this for groups implementation.


FYI: Transfering from Lemmy issue on adding a from a comment by @nutomic :

We are currently doing groups like this:

  • users can follow groups, and the group always accepts the follow
  • if anyone posts to a group, the group will announce that
  • followers get posts and comments via these announces
1 Like

Hi, as @aschrijver already mentioned I am currently working on a Groups app based on Solid and using activity streams vocabulary to handle group joining. For the group itself I am using vcard, because this is already in use in other parts of the Solid ecosystem.

In 5.2.1 Modeling “friend requests” friend requests are modeled as an Offer. This is why I decided to use Offer for group join requests as well in the following way:

  "@context": "",
  "id": "#join-request",
  "type": "Offer",
  "summary": "Alice asks to join the group",
  "actor": "https://alice.example/profile/card#me",
  "object": {
    "id": "#join",
    "type": "Join",
    "actor": "https://alice.example/profile/card#me",
    "object": "https://groups.example/main#we",
    "summary": "Alice joins group"
  "target": "https://groups.example/main#we"

The offer is responded with either Accept/Join or Reject/Join.

What do you think about it from activitypub perspective?

1 Like

I see that Friendica has groups, how are they working?

I don’t like that the group is only a vcard.
type can be an Array in ActivityPub. So you can make it both,
a proper AP actor and a vcard.

And, yes, if groups can accept or reject follow requests, Offer is fine.

See activitystreams spec (par 4.1):

When an implementation uses an extension type that overlaps with a core vocabulary type, the implementation MUST also specify the core vocabulary type.

BTW, I wonder if many current AP impls support an array for ‘type’ currently.

Thanks for the feedback. No big deal, I added the type. is a demo group. You can take a look at the JSON-LD representation using curl:

curl -H 'Accept: application/ld+json'\#we

I do not use the Follow activity, but Join, as I understand following to be something else than becoming member of a group.

1 Like

Another important question: if we implement group moderators, how will their powers translate to ActivityPub? Would removing a post be Undo{Announce}? What about replies?

Also worth noting this issue on pleroma-meta discussing groups

1 Like

Undo{Announce} is what Mastodon, Pleroma, etc send when you undo a repost (boost). I think moderator removing a post from a group should be a Delete signed by the group actor.

1 Like