Groups are a shared context for communications organized around bounded membership in a set of group members. Membership in a group is distinct from following the group; being a follower entails potentially receiving future activities published by an actor, but being a member entails a social role within the group independently of whether activities are received. This document explores ways to create and manage groups within the ActivityPub ecosystem.
In a social setting, activities can be bound to various contexts. These contexts can be things like:
Popular implementations of ActivityPub (and popular social networking and social media services) have unfortunately historically neglected support for binding resources to context, in a problem known as context collapse. In particular, the social ecosystem widely lacks primitives for posts having authorship and audience bound to a consistent but dynamic collective group.
In social psychology, groups are established based on a shared purpose or identity that mediates group activities. For example, users hosted on the same network or service do not necessarily share any common identity. You could describe a "collection of all users on this service", but this is not necessarily the same as a "group". The quality that defines a "group" is its entitativity, or the perception of being a group (from [[[understanding-social-groups]]]). It is only when the Group becomes a "real" "entity" (or is "reified") that we can consider it a Group (and not simply a Collection of actors). We can describe properties of the group separately from properties of its members, and separately from properties of the collection.
The term "Group" is used in other contexts in ways that may or may not align with the use of the word "Group" in this document.
The [[[FOAF]]] is one of the earliest vocabularies to define a concept of a "Group", alongside other social concepts. In particular, relations like "member" and "membershipClass" can be used to describe who is a member of the group. FOAF defines "Group" like so:
The Group class represents a collection of individual agents (and may itself play the role of a Agent, ie. something that can perform actions).
This concept is intentionally quite broad, covering informal and ad-hoc groups, long-lived communities, organizational groups within a workplace, etc. [...]
The [[[RFC6350]]] defines a KIND of "group" like so:
a vCard representing a group of persons or entities. The group's member entities can be other vCards or other types of entities, such as email addresses or web sites. A group vCard will usually contain MEMBER properties to specify the members of the group, but it is not required to. A group vCard without MEMBER properties can be considered an abstract grouping, or one whose members are known empirically (perhaps "IETF Participants" or "Republican U.S. Senators").
All properties in a group vCard apply to the group as a whole, and not to any particular MEMBER. For example, an EMAIL property might specify the address of a mailing list associated with the group, and an IMPP property might refer to a group chat room.
The concept of membership, while optional, is one of the defining features of a group according to vCard. It is possible to describe membership in a group using both the vocabulary of vCard and the vocabulary defined in this document.
The [[[activitystreams-vocabulary]]] defines a Group only in the most general terms:
Represents a formal or informal collective of Actors.
The groups described by this document can be said to be instances of https://www.w3.org/ns/activitystreams#Group, but they carry additional considerations. Activity Streams 2.0 does not define a concept of membership within a "Group".
Note that the membership concepts defined in this document can be used with other types, and the protocols for managing membership depend on the presence of properties rather than the presence of types.
[[[activitypub]]] defines an actor system where activities can be published to outboxes and pushed to addressed audiences as notifications. Primarily, ActivityPub defines a way to manage "followers" via the "Follow" activity, which one can later "Accept" or "Reject":
The follow activity generally is a request to see the objects an actor creates. This makes the Followers collection an appropriate default target for delivery of notifications.
However, binding all social contexts to the concept of "followers" limits the range of social contexts that people can participate in. Being a follower does not carry any inherent social considerations beyond a simple desire to see more activities from a certain actor. Also, the concept of members is distinct from the concept of followers; it is possible to be a member without receiving activities intended for followers.
[[[FEP-1b12]]] uses the Activity Streams 2.0 "Group" term directly, but assumes that any or all "Group" object necessarily adheres to the protocol requirements laid out in the FEP. In FEP-1b12, a "Group" receives activities in its inbox, and publishes activities to its ActivityPub followers where the type is Announce and the object of the Announce is the received activity (instead of forwarding from inbox as defined in ActivityPub).
The groups described in this document are not constrained in which activities they produce. It is possible to produce Announce activities in accordance with the aforementioned FEP, but this is not a requirement of this document.
[[[RFC9420]]] and [[[RFC9750]]] define a notion of a "group" as a shared cryptographic state:
A client that is part of a group is a member of that group. As groups change membership and group or member properties, they advance from one epoch to another and the cryptographic state of the group evolves.
This document's notion of groups similarly allows for binding state to the group context, but without a strong requirement of cryptography or encryption.
Some platforms implement "group" functionality, although it may not be called a "group".
A popular conception of "groups" is as a sort of forum. However, while the Forums task force deals with the organization of posts into threads and/or forums, the Groups task force deals with the organization of actors into groups which have members and roles. Thus, the work of these two task forces is complementary.
Since MLS is designed around a concept of "groups", alignment between this document's model of groups and the MLS model of groups can allow implementation of the group architecture via an MLS group's application data, and opening the possibility of both authenticated and encrypted groups.
https://swicg.github.io/groups/#Memberhttps://swicg.github.io/groups/#membershttps://swicg.github.io/groups/#membershipsFor information about the group:
For voluntarily managing one's own membership:
For managing other people's memberships in a group you are authorized to manage:
ActivityPub defines a flow for Follow, Accept, Reject, and Undo activities with the following states:
And the following state transitions:
This design from ActivityPub has the following shortcomings and issues:
It can seem appealing to mirror the Follow activity flow and its side effects, but with the Join activity instead of the Follow activity -- perhaps assuming that familiarity with the existing Follow flow will make it easier to implement a parallel Join flow. However, doing so inherits all of the aforementioned issues and would lead to a repeat of the implementation errors faced in practice.
To address the shortcomings of the mirrored flow described above, we need a design with the following properties:
The simplest way to create a membership record is to rely on something like HTTP PUT at an indexed location:
PUT /members/alice.example HTTP/1.1
Content-Type: application/activity+json
{
"type": "_:Member",
"_:user": {"id": "https://alice.example"},
"name": "arisu"
}
Alternatively, HTTP POST can be used instead, to let the members collection choose the resulting Location:
POST /members/ HTTP/1.1
Content-Type: application/activity+json
{
"type": "_:Member",
"_:user": {"id": "https://alice.example"},
"name": "arisu"
}
The response might show that the membership is pending:
HTTP/1.1 201 Created
Location: https://group.example/members/01KSQFYKVAGWH0HW09QMTD2VC9
Content-Type: application/activity+json
{
"type": "_:Member",
"_:user": {"id": "https://alice.example"},
"_:pending": true,
"name": "arisu",
}
In this case, the member and the user are not the same -- the member wraps the user. Similarly, metadata about your membership within a group is not metadata of the actor. We can use membership records/entities to achieve this separation.
In MLS, clients are added to groups via Proposal/Commit, and then separately receive a Welcome message that contains the keying material needed to actually join the group. This "add before join" pattern means that the Join does not exist before the client has actually joined the group.
Per MLS Architecture Section 3.7 "Users, Clients, and Groups":
Note that until a client has been added to the group and contributed to the group secret in a manner verifiable by other members of the group, other members cannot assume that the client is a member of the group; for instance, the newly added member might not have received the Welcome message or been unable to decrypt it for some reason.
We could have the new member confirm their membership with a Join activity (which gets republished to the group's outbox), instead of using the Join activity to manage the membership itself.