FEP-1b12: Group federation

This sounds like how ostatus:conversation was/is used in Mastodon, as simple a value (string) that gets copied around. Pleroma in fact uses context for this already, with a link to domain.example/contexts/some-uuid (and this value also gets assigned to conversation, but sadly it is not resolveable)

If you link context to any object, it will be controlled by a single entity, unless you use anonymous objects. In any case, if not context, then which property should be used to link to a collection of all related activities? This is how projects such as Osada, Zap, Streams, etc already use context.

This FEP is mainly describing existing behavior, and aims to be compatible with implementations that do not have group-specific functionality. I understand the appeal, but one goal of the Mastodon proposal is to propose a distinctive UX for groups with different restrictions on posts, so that requires a clean compatibility break, which this FEP does not provide


Otherwise, this seems pretty sensible, although I’m surprised at the use of Page for top-level posts (but that seems to be non-normative?)

Listing top-level posts is interesting, but it being in a replies collection sounds weird.

Curiously, the moderation actions do not seem to include deleting a group post.

1 Like

@cpmoser @ trwnh About context, i dont see any indication that it should be limited to collections. According to Activitypub Vocabulary the range is Object | Link. The example just happens to show “Activities in context 1”, but that doesnt mean that its the only way to use this field.

I can definitely see it being used to reference the thread, or even as an array that contains the thread and all parent comments in case of a nested comment. In general this isnt necessary because you can traverse up the comment chain, but that doesnt work if one of the intermediate replies was deleted. I will add that implementations MAY use the context field for this, without specifying a particular format.

We are implementing forums using groups as the technical basis. So they are not the same, but essentially represent the same concept. Should this be clarified somehow?

Right, my mistake.

We have done exactly that in Lemmy so far, but its awkward in practice. The to field can contain many different things, so to get the group from a given activity you need to loop through to values and try to resolve each one as a group until you find one that works. With audience you just need to take the value of that field and thats it.

Is there any alternative that you can suggest? One way or another the group has to send content to its followers. I guess it could rewrite existing activities and put itself in the actor field, but that seems even more odd.

By the way I dont think there is anything wrong with discussing Yuforium in this thread, its completely relevant.

I read your federation.md, and your context field seems to be identical in purpose to the audience field in this fep. However you use arrays so that a post can belong to multiple forums. I find that overly complex, what we do is provide a “crosspost” button in the ui which creates a new, identical post in another forum.

You also define multiple custom types such as Community, Forum etc. I suggest you find some existing types in Activitypub Vocabulary for those things, otherwise you will have major compatibility problems.

In general i find your federation model a bit confusing. Its not clear to me what exactly these custom types are for, or what kind of functionality you want to build on top of it that is not possible with the data formats in this fep. Is there some existing (centralized) forum software which you would compare it to?

1 Like

Can you explain more how the UX will differ, and why a compatibility break is required? Is it related to private groups? Thats an area where I would definitely like your input to the fep.

Otherwise, this seems pretty sensible, although I’m surprised at the use of Page for top-level posts (but that seems to be non-normative?)

Page is just an example, like it says “Object representations may differ based on the requirements of each application.” I changed this to also mention that types may also differ.

Listing top-level posts is interesting, but it being in a replies collection sounds weird.

Why is that, it seems replies is made exactly for this purpose. Do you have another suggestion?

Curiously, the moderation actions do not seem to include deleting a group post.

We use Delete for that, but @trwnh mentioned that this activity is generally only used for actors to delete their own objects. So i removed that part and left it to each implementation for now. The best alternative is probably Remove.

Latest changes

That depends on what you call a private group. The current proposal in Mastodon is that group posts are public and subject to the group’s moderation, but any interaction related to a group post happens within the group: that is, a group post cannot be reblogged, and all replies to a group post are themselves group posts in the same group. Aiming for compatibility with software which does not have support for such groups means that such software will not respect these restrictions.

That sounds exactly like the groups in this FEP, or Lemmy groups. Only that group posts cant be shared outside the group, which isnt mentioned in the FEP but could be added. My question is how you implement this in practice, it seems impossible to enforce.

You can’t enforce it but you can use vocabulary that everyone interprets the same way. By making compatible activities you forfeit this, since a classic ActivityPub implementation will just see public posts that can be replied to and boosted without any notion of group membership or context. That’s why my proposal uses purposefully incompatible vocabulary.

1 Like

I think the specific thing to watch out for is if you leak a Create Note into someone’s inbox then it gets translated into a status. Alternatively having a Note be dereferenceable / resolvable will also do this. It needs something to make it not a valid status.

Looking at fetch_remote_status_service.rb for example, I see that there aren’t many ways to invalidate a status from being created. One viable method I see is if the first value of attributedTo doesn’t resolve to an actor that can be processed as an Account. The other method I see is if the Create Object that gets generated and passed into create.rb gets rejected somehow.

My understanding is that if a group post from your PR is fetched on older Mastodon, it will be converted to a direct post with no recipients because of the addressing (to) including a collection that isn’t understood (a group’s members). This seems like it works, but I think a more semantic way to do things would be to drop to/cc entirely and use audience.

Aside from that, I’m not entirely sure that you need as many purposeful incompatibilities as have been introduced. The only guarantee you need is that a group post will not be processed into a visible status on “classic” implementations.

Nonetheless, it seems like it’s worth differentiating between the “mailing list” use case of traditional Group actors, versus the “publicly appendable collection” use case that might be associated more with a forum or other primarily fetch-based distribution mechanism. I think there is room for both to coexist, but the distinction should be made clearer.

2 Likes

Specific to this FEP: I think the section under Audience field should be changed like so:

Every activity and object which gets published in a community MUST specify the group actor 's followers collection identifier in the audience field. If the object will be available without authentication, then the audience field MUST also include the ActivityStreams Public magic collection.

This allows differentiating between “targeting for delivery” and “targeting for visibility”.

Current example:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://sally.example.org/a/2"
  "actor": "https://sally.example.org",
  "audience": "https://example.com/my-forum",
  "object": {
    "type": "Page",
    "id": "https://sally.example.org/p/1",
    "attributedTo": "https://sally.example.org",
    "content": "Hello",
    "audience": "https://example.org/my-forum"
  }
}

Changed example:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://sally.example.org/a/2"
  "actor": "https://sally.example.org",
  "to": "https://example.com/my-forum",
  "object": {
    "type": "Page",
    "id": "https://sally.example.org/p/1",
    "attributedTo": "https://sally.example.org",
    "content": "Hello",
    "audience": ["https://example.org/my-forum/members", "Public"]
  }
}

Changes made include:

  • using to on the activity (as this is where delivery should be done)
  • making audience on the object include the group members instead of the group itself (as this is who can see the object)
    • the audience also includes the Public collection in case of posts that are not private to the group and will be visible without authentication
2 Likes

I agree that preventing reblogging should be done with privacy levels (Public), not by adding incompatible types. However it will cause problems with parsing to put it in the audience field. Then to get the group, you will have to loop through and dereference urls until you find one with the right type. On the other hand with audience containing only the group, its very easy to parse as you need to resolve this one item and thats it.

In Lemmy we actually put Public in the object’s to field. Mastodon also puts cc Public which is essentially the same. So if objects in a group should not be reblogged, its enough to leave out the to: Public.

Putting the group members/followers collection as audience makes sense. However its also more complicated for parsing, because you first need to dereference the followers url, and then the group itself.

Ah, no, what I was proposing was basically flipping to and audience. For Lemmy’s purpose, it would be like so:

  • get the group from to/cc (there SHOULD NOT be multiple addressed actors; if there are, then you have to loop or filter for the group actor)
  • get the audience to determine who should be able to see it:
    • the group actor itself? either use the group actor ID or use nothing (as this may be implied by posting to the group’s inbox)
    • the group’s members? use the group’s followers value
    • is it available without auth? add the Public magic collection

That’s true - at least at first. Instances can use a bound context (or multiple bound contexts) at first which they can use to discover each other, and then switch to an unbound context when it’s no longer necessary - for example, when a network grows large enough.

If context is a Service, maybe the outbox (or a similar concept) could be used? The only problem is that the outbox is activities and not a real representation of a thread.

It does bring up another issue (maybe for another thread) - why there isn’t a standardized endpoint specified for Actors that represents the current state/summary of all related objects? It seems like what we have in the outbox is effectively an activity log, when it would be nice to have a list of objects (current state).

For example, a standardized way to get this from an Actor:

{
  "type": "Collection",
  "name": "Sally's content",
  "items": [
    "https://sally.com/note/1",
    "https://sally.com/note/3"
  ]
}

vs what we have in the current outbox:

{
  "type": "Collection",
  "summary": "Sally's outbox",
  "items": [
    "https://sally.com/create/note/1", // activity creates note 1
    "https://sally.com/create/note/2", // activity creates note 2
    "https://sally.com/delete/note/2", // activity deletes note 2
    "https://sally.com/create/note/3", // activity creates note 3
    "https://sally.com/edit/note/1" // sally then edited note 1
  ]
}

edit to expand that a little further, we can define vocabulary used to specify that a context would represent a thread vs. subject matter (for example, what’s being discussed):

{
  "id": "https://sally.com/note/1",
  "name": "My favorite places to hike",
  "context": [
    {
      "type": ["Service", "forum:Thread"],
      "name": "Thread of Sally's Note #1 - 'Favorite Hiking Places'",
      "id": "https://sally.com/thread/1"
    },
    {
      "type": ["Service", "forum:SubjectMatter"],
      "name": "Hiking",
      "id": "https://sally.com/subject-matter/hiking"
    }
  ]
}

Note I use “Subject Matter” here instead of “Topic” (just because that can get confused with Thread).

@trwnh That sounds good, but it goes completely against the way these fields are used now. All projects I know of put Public either in to or cc. Moving it to audience would break compatibility. Both fields are also generally used as arrays, not for single values. On the other hand audience is unused so far, so its still available to use for groups.

It does bring up another issue (maybe for another thread) - why there isn’t a standardized endpoint specified for Actors that represents the current state/summary of all related objects? It seems like what we have in the outbox is effectively an activity log, when it would be nice to have a list of objects (current state).

@cpmoser FEP-0400e adds a wall for this purpose. Looks like the example below, Im thinking of adding it to this FEP (without requiring the usage of Add activities).

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "sm": "http://smithereen.software/ns#",
      "wall": {
        "@id": "sm:wall",
        "@type": "@id"
      },
    }
  ],
  "id": "https://example.com/my-group",
  "wall": "https://example.com/my-group/wall",
  ...
}

i am of the opinion that audience should be used more generally for calculating audience :sweat_smile:

anyway, it should not break backwards compatibility because it is the group’s Announce or Add that is addressed to Public, if the audience includes Public. The activity you send to the group can be private to the group.

I made some new changes to the FEP:

  • Completely rewrite the section for audience property, to give clear justification for its use
  • Reword all other sections for better readability and cleaner text
  • Removed all mentions of private groups as they are not subject of this fep

You can view the changes in this pull request. From my perspective the FEP is close to finished now, and will soon request to have it finalized.

Groups MUST discard any activities which do not include the group identifier in the audience field.

this would close the door on sending group moderation activities, no? i think the original “MUST NOT forward” was better than “MUST discard”. there may be cases where you wish to send an activity that is intended for the group actor and not the group itself.

Implementations SHOULD also accept moderation activities which come from the same instance where the community is hosted, under the assumption that these are sent by instance administrators. These moderation activities also need to be wrapped in Announce by the group.

ActivityPub does not have the concept of an “instance”, so this wording is problematic. also, why should they be wrapped in Announce? that seems completely unnecessary

1 Like

That sentence was quite complicated with the double negative, so I tried to simplify it. I have now decided to remove it, and leave that to each implementation. FYI Lemmy also includes audience with all moderation activities, in order to know which group they belong to.

The moderation section is based on the way Lemmy works, I added a comment to clarify that. Also replaced “instance” with “server”.

Latest changes are here, and I am also requesting to finalize the FEP.

1 Like

@nutomic has requested this FEP to be finalized. If there are no significant objections in the next 14 days, this will be finalized on Feb 8th, 2023.

How do apps that do not understand groups interpret these messages?

I know in Hubzilla, they had to get creative to make forums compatible with Mastodon and other platforms which did not understand groups or forums.

For example, since Mastodon will reject or ignore any messages from people you don’t follow, Hubzilla had to make the forum the owner of the first post in a thread, instead of the actual author. If we did not do this, Mastodon users would not see new posts in the forum.

I know this FEP is meant to address this issue, but I am curious if it is backwards-compatible with platforms that don’t understand what a group or forum is. Or does the new spec only make groups compatible with platforms that understand groups?

3 Likes