FEP-1b12: Group federation

Internet forums are probably the oldest form of social media. They can be implemented with the Activitypub protocol, but such implementations may not necessarily be compatible with each other. This document defines a common subset of Activitypub which is used by different platforms to federate groups in a mutually compatible way.

Full text

4 Likes

Hello!

Internet forums are probably the oldest form of social media. FEP-1b12: Group federation describes how they can be implemented with the ActivityPub protocol.

Please use this thread to discuss the proposed FEP and any potential problems or improvements that can be addressed.

4 Likes
  1. I’m not sure that this needs to be an FEP if it is just defining standard ActivityPub semantics. What does this FEP propose that is not already part of ActivityPub?

  2. From what I can see, the only deviation is that it uses audience instead of to or cc, which is fine by me, but seems like it isn’t the main intention of the FEP.

  3. This part caught my attention:

  • Group implementations SHOULD NOT announce activity types which they are unable to parse and verify, because these might be interpreted in unexpected ways by other platforms whose users follow the group. For example if a group implementation does not support Block activities, it should never forward an Announce/Block activity to its followers, because it might be invalid or malicious. Similarly, incoming Announce activities should be rejected by the group.

  • Why is this a recommendation? If I send an Offer and the Group sends an Announce Offer, is this really an issue if the Group actor doesn’t understand what an Offer is? It sounds like this entire section comes down to implementation details and not any actual recommended behavior. I can think of an example where Announce/Announce actually makes sense – resharing to a group via Announce/Announce/Note. Think of the “crosspost” functionality of something like Reddit. The outer Announce is just a delivery mechanism, and the inner Announce has semantic meaning. Some affordance should be made for this.

  1. The use of moderators property is not part of the ActivityStreams Vocabulary or @context definition, so it needs to be defined somewhere within a proper namespace at minimum. Arguably, this could also be expressed using the existing as:attributedTo property, but more analysis is needed.
  • From my initial understanding, the moderators are managed by the Group actor, so the presence of this field is purely informational and has no actual bearing on the protocol – ideally, the Group actor should be the one to handle Announce/Delete and Announce/Block, and actually, this behavior of respecting a Delete from someone who is not the author of the original resource should be defined in a (separate?) FEP as well.

In summary, I see two FEPs that could be spun out of this:

  1. Using as:audience instead of (or in addition to?) as:to and as:cc
  2. Authority of an actor to make valid as:Delete activities targeting objects not as:attributedTo them

The 1st one seems like a decent idea, but the 2nd one could get really complicated. If there were some Collection that was attributedTo the Group, then the Group actor could Add or Remove objects to that collection instead of trying to Delete them (which may not be possible unless perhaps the object is attributedTo both some Actor as well as the Group). This Add/Remove to Collection behavior is what is being pursued in Mastodon/Pixelfed/Smithereen via FEP-400e or similar, for probably that reason.


cc @nutomic as author of the FEP – I would like to hear more about your thoughts on this feedback

1 Like

I would also like to specifically make a distinction between Groups within current ActivityPub (which are largely mailing lists) vs. an actual internet forum where there is a consistent thread or topic, and messages are added to it as some form of collection. Groups essentially have a single topic or thread or wall. Forums are a collection of topics and threads, each of which are collections of posts.

2 Likes

Thanks for your reply, I will address your points one by one.

I’m not sure that this needs to be an FEP if it is just defining standard ActivityPub semantics. What does this FEP propose that is not already part of ActivityPub?

I dont see any part of the Activitypub spec which defines how Group actors should interact with others. It only mentions how actors work in general, and some specific activities. It seems to be written only with Person-to-Person interactions in mind.

Now there are already many different projects which implement group federation, which mostly happen to be compatible. This document aims to standardize the behaviour, so that its not necessary to test against each project manually, or search each project’s documentation for a description of how it works. An FEP seems like the most appropriate place for this.

Why is this a recommendation? If I send an Offer and the Group sends an Announce Offer, is this really an issue if the Group actor doesn’t understand what an Offer is? It sounds like this entire section comes down to implementation details and not any actual recommended behavior. I can think of an example where Announce/Announce actually makes sense – resharing to a group via Announce/Announce/Note. Think of the “crosspost” functionality of something like Reddit. The outer Announce is just a delivery mechanism, and the inner Announce has semantic meaning. Some affordance should be made for this.

I can change that section to exclude only a specific list of activities which are relevant for moderation. So the group should not announce Block, Delete, Ignore, Move, Remove, Undo/Block, Undo/Delete, Undo/Ignore, Undo/Move, Undo/Remove, unless verified as valid moderation activity. Also some activities like Follow, Accept, Join, Leave should never be announced. And i can remove the part about not announcing Announce activities. Does that sound good?

The use of moderators property is not part of the ActivityStreams Vocabulary or @context definition, so it needs to be defined somewhere within a proper namespace at minimum. Arguably, this could also be expressed using the existing as:attributedTo property, but more analysis is needed.

True, attributedTo makes sense, i can change it to that.

From my initial understanding, the moderators are managed by the Group actor, so the presence of this field is purely informational and has no actual bearing on the protocol – ideally, the Group actor should be the one to handle Announce/Delete and Announce/Block, and actually, this behavior of respecting a Delete from someone who is not the author of the original resource should be defined in a (separate?) FEP as well.

In the Activity Vocabulary there is no mention that Deletes can only be done by the author of the original object. Maybe it helps to think of it not as deleting the object, but as deleting it from the group. This could also be represented with a Remove which has the group as target, but then we would have to specify the group id twice, once in target and in audience.

I still have to talk with devs of other projects with group federation mentioned in the doc. Will ask them how they handle moderation.

I would also like to specifically make a distinction between Groups within current ActivityPub (which are largely mailing lists) vs. an actual internet forum where there is a consistent thread or topic, and messages are added to it as some form of collection. Groups essentially have a single topic or thread or wall. Forums are a collection of topics and threads, each of which are collections of posts.

This is already possible with existing group implementations in all the mentioned projects. Each Group actor represents a forum with a collection of threads (Page in case of Lemmy), each with a collection of comments (Note). An instance can have multiple groups to represent different forums. I will add a section about this to the doc.

Edit, I just uploaded a new draft: fep/fep-1b12.md at 7d3b77bc6a7c4f30a5da47da9827a6be5adebe85 - fep - Codeberg.org

2 Likes

My main concern here would be that this approach seems to be going in a different direction than the other discussions. (see Standardizing on ActivityPub Groups)
I don’t want to end up with two separate ways to implement a group going forward.

Beyond that:

  • The “Announcing activities” thing still seems like a weird Lemmy-ism, though I also never got a clear answer on how activity forwarding is meant to work in general so I suppose this is technically valid
  • “Implementations SHOULD ensure that activities wrapped in this way keep all their original data.”
    I don’t see how this is meaningful, given that the remote server already can’t trust data being forwarded unless you use LDSigs, which to my understanding already require all the fields
  • Don’t love the “moderation activity blacklist”, seems like there ought to be a better option to validate these. Broadcasting follows also might make sense for some implementations, I would leave that up to the to/cc fields in the activity itself
  • As always, Delete belongs to the content author. Something else should be used for group rejection
1 Like

Draft update merged. #27 - FEP 1b12 Specify how threads/comments work, some changes to moderation (ref #22) - fep - Codeberg.org

1 Like

It doesn’t seem that way to me. The definition of those activities and the delivery mechanism is basically enough to do whatever you need. POST to inbox to deliver an activity, and the side effects depend on the server/client that will be processing the received activity. In this case, something like Lemmy is doing both server+client duties.

Imagine an ActivityPub client that, when something arrives in its actor’s inbox, will Announce it. This seems to be the core mechanism of the FEP as written, and this is already defined in ActivityPub, albeit loosely – the definition is focused around resharing and adding to the shares collection. That part is less important than the fetching or distribution+validation behavior, so it is only a SHOULD.

This, I can sympathize with, although it does feel like the FEP process is a bit too heavy for this. Or perhaps it should be reframed to be something along the lines of “these are the current best practices and mechanisms in use”? Although it does seem like there could be a distinction… or maybe a repo for projects to submit FEDERATION.md to, so that there’s one place to browse? In any case, I won’t get too deep into this, as it is not very relevant to what I have to say.

Ah, I think I see your intention here with this section now. I still think it’s an implementation detail, though – perhaps an actor does wish to announce a Follow/Join/Leave/etc, if their associated “client” implementation keeps some sort of activity log or activity stream like Facebook does. The assumption that this information is private doesn’t seem like a universal one – consider a group software which makes its members available.

I would honestly consider leaving that section out, or otherwise removing most of its language – it may be enough to say something like:

  • A Group may choose to not Announce certain Activities, depending on what an implementation considers valid.

Anything else should be added in a non-normative section or kept to FEDERATION.md.

It might, it might not. Depends on if you want the concept of a “group owner” as someone who can Delete the Group. I’m not saying that moderators Collection is a bad idea, I’m just saying it needs to be defined somewhere and given a namespace.

7.4 Delete Activity says otherwise:

  • The side effect of receiving this is that (assuming the object is owned by the sending actor / server) the server receiving the delete activity SHOULD remove its representation of the object with the same id, and MAY replace that representation with a Tombstone object.

The assumption of ownership is important, as it prevents you from going around deleting other people’s objects. If an object exists within an actor’s namespace, it is owned by the actor and only that actor can delete it. If an object exists within a server’s namespace (or if an actor is managed or owned by a server), then only that server can delete it.

Yeah, this is why proposals like FEP-400e use Add/Remove instead. Removing something from a Group’s Collection is similar to deleting it, but more accurate – the object still exists as far as the original actor is concerned, it’s just no longer visible to the Group.

But as stated before, there is also room to write an FEP concerning Deletes from other actors being respected.

Tangential, but you could use Offer Remove from a moderator, and then the Group validates that this request came from a moderator, and authors its own Remove activity. Something like this:

  • actor: alice
    type: Offer
    object:
      actor: group
      type: Remove
      object: bad-post
      target: group/posts (or group/post/replies)
    

The Group would then, assuming the activity is valid, send out:

  •   actor: group
      type: Remove
      object: bad-post
      target: group/posts (or group/post/replies)
    

It could also optionally send an Accept Offer to the moderator to let them know that the action was successful.

What about nesting? To me, a proper “forum” has categories and sub-categories and eventually threads, which are just Collections of Articles/Notes or whatever. Using a Group as a “forum” is different to me. But that’s a bit of a distraction

I’ll leave my updated thoughts on that in a new reply after I’ve read it.

1 Like

OK, seems like the changes to the draft are primarily about “Threads and comments”, which are again fairly standard to current fedi. Perhaps it would be useful to define usage for the context property or define a conversation property that contains all… objects or activities? within a given thread (“Page”)

1 Like

My main concern here would be that this approach seems to be going in a different direction than the other discussions. (see Standardizing on ActivityPub Groups )
I don’t want to end up with two separate ways to implement a group going forward.

I dont remember all details from that thread, but the main conclusion seems to be using FEP-400e, right? My problem with that is that there isnt yet any implementation which supports it in production (afaik), whereas the approach in this FEP is already used by multiple projects.

The “Announcing activities” thing still seems like a weird Lemmy-ism, though I also never got a clear answer on how activity forwarding is meant to work in general so I suppose this is technically valid

Doent lotide and other projects use the same approach? Is there any alternative you would suggest?

  • “Implementations SHOULD ensure that activities wrapped in this way keep all their original data.”
    I don’t see how this is meaningful, given that the remote server already can’t trust data being forwarded unless you use LDSigs, which to my understanding already require all the fields

The way it works in Lemmy now is that the activity gets deserialized first, and then deserialized for announce. During the deserialize step, fields which Lemmy doesnt know about are dropped. This is what I want to avoid, but maybe its not so important. Do you have any link about LD signatures for activities? Might be worth mentioning this as well.

Edit: FEP-8b21 describes a similar concept, I will include a link to that.

  • Don’t love the “moderation activity blacklist”, seems like there ought to be a better option to validate these. Broadcasting follows also might make sense for some implementations, I would leave that up to the to/cc fields in the activity itself
  • As always, Delete belongs to the content author. Something else should be used for group rejection

Okay Im rewriting this section so that moderation is optional, and can be entirely ignored by implementations. Im also removing mention of Delete as moderation activity, and leaving it to each implementation what it considers as moderation activity. Validation will be done by checking the actor against the group’s attributedTo instead.

Yes its possible, but the details of it are not specified in the context of groups. Thats what Im doing here. And there is no client involved, everything is done on the server.

This, I can sympathize with, although it does feel like the FEP process is a bit too heavy for this. Or perhaps it should be reframed to be something along the lines of “these are the current best practices and mechanisms in use”? Although it does seem like there could be a distinction… or maybe a repo for projects to submit FEDERATION.md to, so that there’s one place to browse? In any case, I won’t get too deep into this, as it is not very relevant to what I have to say.

Federation.md seems much more specific, as in describing which exact activities with which fields an implementation sends. This FEP aims to document the common things which are necessary for different implementations to federate groups between them.

By reframing you mean to change the summary? I can do that if you have any suggestion.

About moderation: like I said in my previous comment, Im changing that section to make it entirely optional, and remove the activity blacklist. Im also removing the mention of Delete as moderation activity and leaving that to implementations for now.

What about nesting? To me, a proper “forum” has categories and sub-categories and eventually threads, which are just Collections of Articles/Notes or whatever. Using a Group as a “forum” is different to me. But that’s a bit of a distraction

This could be represented by collections of groups. But lets stick to the basics for now.

OK, seems like the changes to the draft are primarily about “Threads and comments”, which are again fairly standard to current fedi. Perhaps it would be useful to define usage for the context property or define a conversation property that contains all… objects or activities? within a given thread (“Page”)

True, I will add that.

Edit: new changes submitted

I meant Lemmy is fulfilling the role of both ActivityPub Server + ActivityPub Client – and also, a client can be an application running on a server, but that’s besides the point.

The point is that “the details […] in the context of groups” is mostly specified – this FEP is describing standard ActivityPub behaviors. The spec isn’t only for Persons. It’s for anything that can receive or send a POST to inbox. So sending an Announce of something that you received is nothing new, really.

By “reframing” I mean what you did right here in the quoted section – providing the context that this FEP is about documenting what is currently done rather than coming up with some extension.

From what I see of these changes:

replies

It’s actually a very interesting idea to use the replies collection for all posts instead of only top-level replies. I’m not sure how I feel about it, but I’m going to think about it some more. I am inclined to say that based on current implementations and behaviors, it would make more sense to use context as a collection of all Activities within a given “thread”, and keep replies for immediate top-level replies.

group moderators

I’d undo the change explaining group moderators, what you had before was actually better:

Group moderators are those actors who control the group, are able to change its metadata and remove malicious content. They are listed in the group’s attributedTo collection

And again, I’d also consider using Offer for moderation, but the important thing is to phrase it something like this:

Implementations may choose not to Announce some activities. For example, certain activities may represent implementation-specific private actions.

There’s also the option of simply not including audience on such activities? Maybe the Group should only Announce an activity it receives that has audience equal to the Group identifier? That’s worth considering, let me know what you think

EDIT: I think I like this idea actually, so I’d propose this change in bold:

When a group receives a activity in its inbox, it SHOULD perform some automatic validation, such as checking for domain and user blocks. Groups MAY require additional validation, such as accepting content only from followers, or even manual approval from group moderators. In case an activity fails these checks, the group MAY respond to the sender with a Reject activity. Groups MUST NOT forward any activities that do not have an audience which includes the group actor identifier.

Okay im rewrote the summary.

It’s actually a very interesting idea to use the replies collection for all posts instead of only top-level replies. I’m not sure how I feel about it, but I’m going to think about it some more. I am inclined to say that based on current implementations and behaviors, it would make more sense to use context as a collection of all Activities within a given “thread”, and keep replies for immediate top-level replies.

You mean that thread’s replies collection should only include top-level comments? That makes sense, Im updating it. Also im replacing “post” with “thread”, otherwise its too ambiguous.

Groups MUST NOT forward any activities that do not have an audience which includes the group actor identifier.

This is effectively already specified by having the audience field mandatory, but it cant hurt to emphasize. Also added your other suggestions for moderation.

PR is here: #32 - FEP-8b32: Update proposal - fep - Codeberg.org

We had some discussions a few months ago in some of the developer meetings about using the context field for communities and forums (also created this document - activitypub-docs/federation.md at b2f7a79831ff20610f331a4bf8ba288d7ee1a998 · yuforium/activitypub-docs · GitHub). Recently I redid a few things in Yuforium so haven’t done the POC yet, but once federation is working properly I’m going to start working on the implementation mentioned in that document (will also start working on the FEP as well).

that seems wrong – my reading of the spec definition is that context should be a Collection. the use of context for arbitrary objects seems potentially problematic, despite being legal.

i’ve oft considered using it for defining the “root post in a thread”, sort of like an originating context, but i think it is better to use it as a grouping and therefore have that IRI resolve to a Collection.

Based on the description and example they give for context:

The notion of “context” used is intentionally vague. The intended function is to serve as a means of grouping objects and activities that share a common originating context or purpose. An example could be all activities relating to a common project or event.

I don’t see this as being tied to a Collection - An Object it might have multiple contexts. Forcing it to be a Collection definitely is problematic here - if multiple Collections are specified as the context, is this the best representation of a common originating purpose like a project or event?

Reading this I would see context as a pointer to another Object (like an Event). I wouldn’t want to coerce that to be a Collection. Keeping it as an Object lets it be a more general, as the docs point out it should be intentionally vague.

It seems to me that context is the field for content grouping. One of the primary uses of forums is to organize around a specific event or project and this fits the example perfectly. It could easily be expanded to include many of the other things that people organize communities/forums around (hobbies, parent groups, games, sports, etc.).

That being said, I’m also not sure it’s necessary to store or pass around the root post of a thread as the context, since that can be traversed by any federated instance to get it (or helped by other instances to find it).

inReplyTo is not functional, so if it’s really necessary, another instance could provide a pointer to indicate it (if it knows the root). For example:

{
  'type': 'Note',
  'content': 'Reply to one of the replies in the thread',
  'inReplyTo': [
    'https://instanceof-reply.com/user/chris/note/5',
    {
      'id': 'https://instanceof-root.com/user/chris/article/1',
      'inReplyTo': []
    }
  ]
}

I wouldn’t force it to be a Collection (not a MUST) but it seems like a good idea to make it specifically resolve to the collection of all activities within that context (SHOULD) – you can see from Example 73 the “intended usage” of this property…

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "summary": "Activities in context 1",
  "type": "Collection",
  "items": [
    {
      "type": "Offer",
      "actor": "http://sally.example.org",
      "object": "http://example.org/posts/1",
      "target": "http://john.example.org",
      "context": "http://example.org/contexts/1"
    },
    {
      "type": "Like",
      "actor": "http://joe.example.org",
      "object": "http://example.org/posts/2",
      "context": "http://example.org/contexts/1"
    }
  ]
}

Note that this example does not assign id to be http://example.org/contexts/1 but it seems natural enough. At the very least, there should be a property for serving up this “Collection of all activities within a context”, and if it’s not context, then what should it be?

It seems quite useful to have some way to get to the root post if the thread and the context are the same thing, i.e. if the replies exist in context of that post. It also seems quite useful to have a Collection of all Activities related to some common origin. But I’m not sure about how useful it would be to use it as seen in the examples within that federation.md you linked. What does it mean to have a context of some actor? What even is a Topic supposed to represent? It feels like in every example there, the use of tag might be better.

Even so, I feel like discussing Yuforium is off-topic to this FEP (and I’m not convinced that the “forum” and “group” use-cases are 100% equivalent).

Ok, not trying to make this about Yuforium, but if we’re going to start talking about using context to store thread structure metadata I think it’s relevant. As far as the FEP:

  1. I think the FEP is conflating Groups and Forums. It’s detailing the Group as a collective actor which is a good thing, but I wouldn’t consider a Group as the same thing as a Forum even though they’re closely related.

  2. “Comments MUST provide a field inReplyTo linking to the thread they belong to.” - I don’t think this is right, inReplyTo should reference the post you’re replying to - and that’s not necessarily the root post. Is this intentional?

  3. I’m not sure what the purpose of using audience is - what’s the reason to use that and not the to field?

  4. Wrapping everything inside an Announce activity seems a bit odd, but maybe I need to think about that one a little more.

Regarding the context field:

There’s no mention that the Collection in the example can or should represent the full scope of the context. If we accept that it resolves to all activities within that context then a single entity controls that. That’s going to break zero trust federation of content across instances.

To federate this way, I suppose you could have multiple Collections representing each instance in the context, but at the end of the day you’re going to end up with a very large context field if you federate across 100s of instances. You could also have a single context that is a pointer to multiple instances, but that’s sort of what I’m suggesting with Topic (a little more on this in a bit).

I suggest using context in a more ephemeral way - something that can be passed around and used to string content together rather than anchoring it to a single authority. Any instance should be able to freely associate with a context and federate its content, it should be up to other instances whether or not they want to accept and display that content.

Regarding context vs. tag, I agree that tags could be used to federate content. But I think it’s important to create a distinction between how content is described (tags) vs. how it is federated and aggregated across instances (context).

About Topic with regards to context:

It’s simply an ID for instances to aggregate and federate via the context field, the only important point being that it’s an Actor. So any Actor type would be fine, I just used the word “topic” to represent subject matter being discussed. That fits the spec of “activities relating to a common project or event”. (and I’m not trying to suggest that context has to be an Actor, just that it could be).

Actors can have followers, so it’s a simple and effective means of discovery across instances. Instances can use this to bootstrap a multi instance forum, and switch to an unbounded context once a network of instances grows large enough.

Again, not trying to make this about Yuforium, but thought it important to bring up context. Will revisit my doc and submit as a FEP so it can be discussed here separately.

1 Like

i agree that inReplyTo should point either to the immediate parent comment or to the root Page – and ironically, context could work for pointing to the Page that the comment belongs to. But again, perhaps some other property is better for this “root object” use case.

You may deliver an activity to some actor that is not in audience. Whereas audience signals the intended audience for an activity or object, to is specifically for delivery. This opens the door to having activities delivered to the Group actor but not to its audience/members. This is especially important because the manner by which moderation is signalled is not yet included in the FEP.

But also more generally, to and cc should not be confused with audience, and it is a shame that many implementations do confuse them, leading to heuristics-based visibility.

This preserves authority. An Announce can either point to a dereferenceable object, or you could have signatures being attached. But signatures are generally not a good idea because they make the activity non-repudiable until the actor keys get rotated, after which point the signature becomes useless. It’s easier to have actors simply delete anything they wish to repudiate, without necessarily worrying about key rotation.