How do we handle Groups (Reconciling FEP-400e and FEP-1b12)?

This is a topic to track the FEP-400e and FEP-1b12 reconciliation, aka “How do we handle Groups”. I’ve made this a wiki to let implementers describe their current status quo


Currently Discourse implements FEP-1b12 to federate posts associated with Category and Tags, i.e. Activities are Announced by a Category or Tag actor.


NodeBB implements FEP-1b12 to federate posts associated with a category (tag actors TBD).


Lemmy implements FEP-1b12

[Add your implementation in here]


Adding on a complication: there is nothing actually special about the Group type in Activity Streams 2.0. It is an anti-pattern to assume support for specific protocols based solely on whether the actor is a Group or not. With that said:

  • You can generally assume 400e or some variant if the actor contains the wall property from Smithereen’s namespace. This property is used by Smithereen (which uses 400e) and also by the Mastodon PR for Groups (which in its current state uses 400e against the Smithereen wall).

  • You don’t really have a signal ahead-of-time that an actor is using 1b12. You can guess that maybe it is, if it advertises itself as a Group and then after you follow it you start receiving Announce/Create/Object instead of Announce/Object or Create/Object. But even this isn’t a 100% guarantee; some relay actors may use the Announce-wrapping-activity behavior as well (albeit inconsistently).

1 Like

To my knowledge FEP-400e is only implemented by Smithereen which has a total of 25 monthly active users.

FEP-1b12 describes the mechanisms used by the vast majority of the Fediverse, namely Lemmy, Friendica, Hubzilla, Lotide Peertube, KBin and more. So there is really nothing to discuss, FEP-1b12 is the de-facto standard. Though it only describes the core mechanism, there are some aspects which arent documented yet. For example Lemmy supports adding and removing moderators, moderators living on a different instance from the community, reports, featuring or locking posts, post removals by mods, bans with content removal etc.

I agree with some of what you’ve said, insofar as FEP-1b12 is a good description of how Group actors are typically used, indeed that’s why I followed it in the Discourse plugin (and appreciate you writing it!). However I would note that:

  1. FEP-400e is not just limited to Smithereen. For example it is the basis of Mastodon’s outstanding Group implementation: Add groups support by ClearlyClaire · Pull Request #19059 · mastodon/mastodon · GitHub;

  2. FEP-400e is not in direct conflict with FEP-1b12, insofar as it’s about collections associated with a Group, as opposed to Group federation, but nevertheless can potentially cause conflicts due to using the same actor type (i.e. Group) for different purposes.

I think that 2 in particular is indicative of what needs discussing here, as can be seen in the examples raised by @trwnh.

To give an example of a potential conflict in the vein of 2, the Discourse plugin is now using Group actors for taxonomies (i.e. Categories and Tags). Discourse also contains a separate concept of a “Group”, namely a group of users (see for example: Those groups of users would be a closer analogue to Mastodon’s current Group implementation (not exact, but closer). If those groups of users were to be turned into ActivityPub actors, it is not yet clear to me how that would be disambiguated from the use of Group actors for taxonomies. I think there may be analogous issues in other platforms.

In RDF you would use a class or type for this. So for example imagine a hypothetical type which is distinct from in that the former has the semantic meaning of “this actor implements 1b12 group federation” while the latter has the semantic meaning of “this actor represents a collective of some kind”. So you could have an AS2 Group actor for your band of musicians, and it would not implement 1b12 (or even necessarily 400e). It would instead use Create like any other typical authoring/(micro)blogging actor. This is an issue that arises only when you assume Group always means what you think it means, which breaks the open-world assumption.

For this I would cite FEP-7888 on the use of as:context, which shouldn’t conflict in any ways, as it does not assume any actor type at all. For the uninitiated, 7888 and 400e are similar proposals, except 7888 uses the “correct” property. (target is invalid on non-activities, and the purpose of context is precisely the grouping behavior intended by 400e. Ironically, 400e’s summary describes itself as a “generic way to signify that an object was created as part of a collection and should only be considered in its context.”… emphasis mine.) 7888 also goes into details about every possible case you might encounter, such as missing context, unresolvable context, unattributed context, and finally, in the interesting case of resolvable and attributable context, it proposes some best-practice behavior for ensuring you send your message to the “right” actor. I’m not yet sure if there needs to be explicit signaling of 7888 semantics, as it’s mostly informational, but I could see the possibility of spinning out the normative recommendations as a separate FEP if there are indeed additional semantics that should be signaled.

For what it’s worth, 7888 is more or less aligned with Pleroma/Akkoma and Osada/Zap/Streams on how they use context. Mastodon uses the deprecated ostatus:conversation for the same purpose, although it could switch over in the future.


FEP-400e is also the basis of Conversation Containers in Streams (and soon probably Hubzilla):

As far as I know, In Streams this mechanism is not used specifically for groups, but for all kinds of conversations.

@nutomic what do you think of it? Could it be useful for e.g. implementing private groups in Lemmy, or FEP-1b12 will be enough? (I remember some discussion about FEP-1b12 being not well-suited for private groups.)

1 Like

@trwnh Those sound like good solutions! And I would second @silverpill’s observations.

To step back a bit, the thing we’re attempting to do here - and this is more of a continued response to @nutomic’s query about what the point of this discussion is - is to work through the practical implementation of the existing Group-related specifications, and their implications.

As an implementor, the current set of FEP relevant to groups (FEP-1b12, FEP-400e and, indeed, FEP-7888) can be a bit confusing to straighten out. One of the main purposes of the Threadiverse Working Group is to make implementation a bit more of a collective / consensus driven effort to improve ActivityPub interoperability in this scope (i.e. between forums etc).

That may call for new FEPs in certain cases, but in this particular case (i.e. Group-related standards) I doubt we’ll be trying to create new standards - there’s already a wealth of them - but more to work through a slightly more consensus-driven approach to implementation. There will be a limit to consensus here too, but I think we can do slightly better than the status quo (in terms of how these standards are being implemented).

@devnull One thing this is making me think is that the W3C task force could produce “implementation guidelines” that exist at a more “product” level, describing how to navigate the FEP landscape in an area like this, perhaps with some examples.

The utility of something like that would be that when, for example Flarum, get to this point in their development cycle, Daniël won’t need to spend a month or so trying to parse how we’ve already navigated this landscape. Or, perhaps, the Mastodon implementation could take account of this kind of work before it’s merged. That will, hopefully, result in a greater degree of interoperability in the end.


Private groups work fine with 1b12. Basically the Group object itself remains public, but activities and objects announced by it are private. New group followers need to be approved manually by moderators, and only followers will see content in the group. I wrote an RFC with details about the Lemmy implementation.

I dont have time to read the Containers link, but anyway we are not going to rewrite the Lemmy federation to something different, what we have works just fine.

1 Like

I want to specifically highlight this quote, because it is indicative of the fact that a lot of maintainers implement ActivityPub not as their day job, but because they want to, strangers sitting in an ivory tower be damned. Sometimes (a lot of the time for me), there just aren’t enough hours in the day.

So 1b12 works well for Lemmy, that’s great, and there’s no reason we ought to try to change @nutomic’s mind.

If at some later point the consensus seems to be that 400e or 7888 might be a standard gaining traction, then he is welcome to reconsider on his own time. As long as he is not excluded!


Agreed, I feel like submitting a normative/prescriptive report would just lead to existing implementors actively ignoring them.

That’s why I mentioned (offline) that we should conduct surveys to make sure we know what the implementation landscape looks like at current before attempting to write any guidelines.

1 Like

Exactly, the purpose of standards and FEPs is to describe how these things are already handled in practice. Not prescribe how it should be done, because implementers wont know and it may not actually work. And like I mentioned before, FEP-1b12 describes the way half a dozen major Fediverse projects work, there is no way they will all switch to a different implementation for zero practical benefit.

Absolutely! We’re not trying to create a different implementation. We all have adopted FEP-1b12 already :slight_smile: The goal is to understand how that works with the other standards which handle different, but related, concepts.

I’m not sure what all of this has to do with the original topic (reconciling FEP-400e and FEP-1b12). If you want to use FEP-1b12, that’s fine. Others might prefer a different mechanism if it works better for their application.

My question was specifically about private groups. Implementations based on FEP-400e already exist in the Fediverse. As far as I can tell, private groups based on FEP-1b12 exist only in a form of an RFC. So, unless there is some architectural flaw in FEP-400e, I don’t really see a reason to use FEP-1b12 for private groups.

Regarding FEPs: they do not describe how things are handled in practice. FEPs are proposals, and can be about anything, as long as it is related to Fediverse.

1 Like

I think what @nutomic meant was that FEP-1b12 attempts to summarise the most widely used approach to federation by Group actors, which I think it does.

Nevertheless, as an implementor you’re still faced with some questions like @silverpill’s (i.e. about private groups), or how to deal with Mastodon’s pending implementation, or how to think about this with respect to FEP-400e and FEP-7888. Navigating that thicket in a consistent way across the various implementations in Threadiverse Working Group is the goal here.

We definitely don’t want to create another standard :slight_smile: (que xkcd comic about a standard to standardise the existing standards).

It is true that many implementations of public groups used Announce activity to distribute posts before FEP-1b12, but FEP-1b12 also introduced additional protocol features. I don’t know how widely these features are implemented today (perhaps this is something the working group could include in the report?).


So I have to raise a tangential point here, which @devnull will probably be interested in as well: this is kind of what I set out to do in the WIP FEP i set out to start writing a week or two ago; at first it was 9988 “Federated Forums” to try to develop UX guidelines and map them to protocol affordances, but this eventually morphed into 8007 “A conformance profile for social networking services” (both titles pending of course). The general idea is that we all do certain things, and they map onto certain concepts, so we should map those common abstractions and primitives and try to concisely bound behavior of implementations such that you can actually know what to expect.

I talk about this in 7888 a bit more, but there isn’t actually a difference between the various spins of fediverse software. They’re all just specific presentations of generic data. Or at least, on a protocol level, the abstractions of one specific presentation shouldn’t leak in. What you have is objects or links, activities, collections. Those building blocks are then transformed into local entities, sure, but the primary focus should be on the protocol level building blocks and not on what any one platform assumes at a UX level. This is how you build commonalities. Practically speaking, a Note can be presented as a social media status or microblog entry or chat message or forum post or comment or whatever. And a Collection of Notes can be presented as a profile, thread, timeline, category, chat room, topic, et cetera. If the grouping is done with an explicit purpose, it ought to be declared as the context.

With that said, there isn’t really anything to “reconcile” about contextual grouping and distribution of activities. They’re mostly orthogonal. The one point of intersection is whether any given actor will send out an Add or an Announce. And given that this is a working group for threaded conversations, it makes sense to me that the working group ought to describe how conversations are constructed. My personal opinion is that these conversations should be explicit based on context rather than implicit based on inReplyTo. Case in point: Consider the common forum use-case of “splitting a thread”, which involves moving a post to another thread. That post may be a reply to a post from the original thread. It’s important to be able to specify the intended grouping as a separate thing from who you’re responding to.

4 Likes said:

My personal opinion is that these conversations should be explicit based on context rather than implicit based on inReplyTo.

Yes, that makes a lot of sense. The building of a context via inReplyTo is decently reliable but suffers from scaling issues. Given a superior method of deriving context (that is, having one provided by the object) would make things simpler, and more complete, if combined with a good backfill method.


@trwnh Thanks, this is very helpful. I agree with pretty much everything you’ve said. So from a practical perspective, for the purposes of implementation, this topic (i.e. reconciling FEP-400e and FEP-1b12) essentially boils down to

I think it would be helpful to work through an example of where this particular point of intersection may become an issue.

So, this would result in something like:

  1. You receive Note 2 (with context A, a collection) which is inReplyTo Note 1 (with context A)
  2. You receive an Update to Note 2 and it now has context B (a collection)?
  3. OR perhaps you receive an Add of Note 2 to context B?

In both 2 and 3, the inReplyTo would still be to Note 1, which would still have context A. @trwnh something like that?


Yeah, pretty much.

Say you encounter an actor of type Group. You Follow the actor. You can’t expect it to behave in any one particular way, but it will generally behave in one of these ways:

  • It is a “normal” publishing actor. You will receive Create Object, Announce Object, Like Object, and so on.
  • It is a boost bot. You will receive Announce Object for any object sent to it, mentioning it, etc.
  • It is a 1b12 actor. You will receive Announce Activity, and other activities described in 1b12.
  • It is a Smithereen-style actor. There will be a wall property, and it will send out Add activities where the target is that wall. The Add.object will also have a target, and this will match the as per 400e.
  • Bonus: It is a context moderator. You will receive Add activities where the target is the object.context. It may also additionally be a “normal” publishing actor.

So for any given “post”, it might be a Create/Add/Announce/Announce-Create. This isn’t limited to Group actors, but it is most prevalent in Group actors because of the popular (erroneous) interpretation of Group as equivalent to Facebook Groups. One final thing to note is that “normal” publishing actors and context moderators may be any type, not just Group – there is no specific type dependency for these mechanisms, as they are the intended mechanisms.


id: <object-1>
type: Note
attributedTo: <actor>
context: <context-1>
content: "I'm posting in a thread"


id: <object-2>
type: Note
attributedTo: <actor>
inReplyTo: <object-1>
context: <context-2>
content: "This post is in a different thread but still replying to the first"


id: <object-3>
type: Article
attributedTo: <actor>
inReplyTo: <object-1>
name: "Some Article"
summary: "In which I reply to a forum post with an entire blog post. This is not part of any context."
content: "<p>Lorem ipsum dolor sit amet...</p>"