How do we handle Groups (Reconciling FEP-400e and FEP-1b12)?
-
Angus McLeodreplied to Gregory on last edited by [email protected]
Great to see you here again!
grishka:And it’s also fine to be incompatible
True! Albeit, I think the general consensus here is that there isn't an inherent incompatibility between 400e and 1b12. The question is more how an implementor approaches processing a Group actor's activities. @trwnh helpfully lays out the possibilities above
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 Implementations See also
SocialHub (socialhub.activitypub.rocks)
The point of this topic and the spreadsheet I just shared is not to contrast the two standards. It's more of an empirical reference of sorts for implmentors.
grishka:you can’t exactly imagine Mastodon and a phpBB forum interoperating in any meaningful capacity simply because their user experiences are so disparate.
Well, I might slightly disagree with you there as Discourse, NodeBB and other forum-like implementations in the #activitypub:threadiverse-wg interoperate with Mastodon
silverpill:Added “Yes (partial)” to FEP-1b12 column.
Thanks!
silverpill:I intend to fully support both FEP-1b12 and FEP-400e in the future.
I think this is where the Discourse plugin will end up too.
Actually, now that I think of it, the Discourse plugin does partially support 400e as it will recognise a Collection in the
target
property. However it doesn't processAdd
itions to such a collection, so saying it supports 400e is premature. -
Actually, now that I think of it, the Discourse plugin does partially support 400e as it will recognise a Collection in the target property. However it doesn't process Additions to such a collection, so saying it supports 400e is premature.
This is where there is some potential to trailblaze as the other half of the equation might be implementing 7888, aka a resolvable
context
.I have a feeling that context and target would work well to point to the same thing.
@[email protected] said in How do we handle Groups (Reconciling FEP-400e and FEP-1b12)?:
By-the-by it strikes me that these implementation spreadsheets we're making should be synthesised in some way at some point.
My assumption was that the surveys and spreadsheets would be helpful to guide discussion at WG meetings, but eventually lead to a SocialCG report of non-normative findings, followed by a recommendation for new implementors.
-
Angus McLeodreplied to julian on last edited by [email protected]julian:
This is where there is some potential to trailblaze as the other half of the equation might be implementing 7888, aka a resolvable
context
.I have a feeling that context and target would work well to point to the same thing.
Agreed! The Discourse plugin treats them as equivalents, with a preference for context over target.
def create_collection(post) # See https://codeberg.org/fediverse/fep/src/branch/main/fep/400e/fep-400e.md # See https://socialhub.activitypub.rocks/t/standardizing-on-activitypub-groups/1984 raw_collection = object.context || object.target ...
julian:My assumption was that the surveys and spreadsheets would be helpful to guide discussion at WG meetings, but eventually lead to a SocialCG report of non-normative findings, followed by a recommendation for new implementors.
Also agreed, that is my thinking too. I just meant that when I was making the spreadsheet it occurred to me that it could also be the basis for an academic project. Perhaps I'm also just being lazy and thinking of who would be enthusiastic about and have the time to do this kind of thing comprehensively.
-
grishka:
My main idea with FEP-400e was to avoid posting group posts to one’s own profile at all costs.
You can't prevent this, because there's no such thing as an explicit "profile" that you add posts into. I don't really understand why Smithereen and Mastodon are so strongly concerned with this point that it leads to some frankly very strange decisions. There are two "correct" paths forward:
- Explicit manage your "profile" stream as a Collection
- Use something like
audience
to hint which "streams" the activity should be displayed in (see https://socialhub.activitypub.rocks/t/overlapping-taxonomies-and-the-audience-property/4229/8 and https://socialhub.activitypub.rocks/t/overlapping-taxonomies-and-the-audience-property/4229/11 for more details)
Really, my concern is that the use of
grishka:target
is not only spec-incompliant, it's also not a guarantee of the thing you're trying to guarantee. If you really want to make sure posts don't get displayed "at all costs" then why not stop using to/cc for such posts? As far as I can tell, Mastodon at least will ignore theaudience
property and should discard your "group" post as having no recipients.it’s not just walls — it’s a generic mechanism of creating objects into someone else’s collections, while also relinquishing full control over them. My FEP explicitly says that the collection owner can delete someone else’s objects contained in the collection.
This also strikes me as unnecessary. There's no need to delete other people's objects, you can just not include them; they effectively become orphaned references that can be garbage collected (or added back later!)
-
trwnh:
If you really want to make sure posts don’t get displayed “at all costs” then why not stop using to/cc for such posts?
I don't address them to any followers and don't put them into the outbox. This should be enough, in theory.
trwnh:This also strikes me as unnecessary. There’s no need to delete other people’s objects, you can just not include them; they effectively become orphaned references that can be garbage collected (or added back later!)
How would you prevent that from leaking into the UX? How would you explain that to users? It's generally expected that group moderators can delete any content from the group. And again, the semantics of you not owning the post, not having a complete authority over it, are important.
-
Is there a practical difference between "deleting from the group" or "removing from the group"? In both cases the end result is that the post is not visible anymore. Semantically it's a red herring, you should own the collection, not other people's posts.
The most basic of examples: I make a static HTML page that I claim is in response to one of your static HTML pages. You have the option to acknowledge my page as a response, or otherwise not acknowledge it. But you can't stop my page from existing. The problem is entirely conventional and third-party observers should view only your page and its acknowledged responses if they care about your authority to decide what gets acknowledged. In other words, "moderation" is in what you choose to include or exclude, not in preventing the existence of something.
The way I'd explain it to users is very simple: "Your post has been removed from the group". UX-wise, you can invalidate the stale cache for the orphaned object. I don't see why anything has to "leak" anywhere.
-
[email protected]replied to Angus McLeod on last edited by
I agree that group actors should not be able to
Delete
posts, onlyRemove
them from a "wall" or acontext
. It should be always clear who owns what. Without that, authentication and authorization are difficult and that leads to numerous security issues.@grishka I'm writing a FEP on this very topic: https://codeberg.org/silverpill/feps/src/branch/main/c7d3/fep-c7d3.md
-
trwnh:
The way I’d explain it to users is very simple: “Your post has been removed from the group”. UX-wise, you can invalidate the stale cache for the orphaned object. I don’t see why anything has to “leak” anywhere.
Well then for me,
silverpill:Remove{Note}
would simply be a fancier way for moderators to do the same thing thatDelete{Note}
would do.It should be always clear who owns what. Without that, authentication and authorization are difficult and that leads to numerous security issues.
It is clear — the group owns its wall collection but each post author also owns each individual post they made, to a degree. The group can't edit posts, but it can delete them. The author can both edit and delete their own posts.
I also have a mechanism for checking that the object is truly in a collection, but that isn't documented as a FEP (yet?). It is documented in my federation document.
-
[email protected]replied to Gregory on last edited by [email protected]grishka:
The group can’t edit posts, but it can delete them. The author can both edit and delete their own posts.
But how recipients are expected to know that?
In a regular
Delete{Note}
activity, authorization check is straightforward:Delete.actor == Note.attributedTo
.But ifDelete.actor
is a different actor (a group actor), then I wouldn't be able to process it without 1) knowing the type of application I'm federating with 2) discovering the members collection.Remove{Note}
, in comparison, doesn't require any special knowledge. It updates thetarget
collection (according to ActivityPub spec), so I just need to verify thatRemove.actor == Remove.target.attributedTo
. -
silverpill:
But how recipients are expected to know that?
It's specified like that in FEP-400e ¯\\\(ツ)\/¯
You don't need to discover the members of the collection to make this work. Remember, I treat ActivityPub like an API.
If you already have this post in your local database, you know which wall it's on, so you check that, in AP terms,
Delete.actor == post.target.attributedTo || Delete.actor == post.attributedTo
(my actual DB schema is different, I haveowner_id
andauthor_id
in my posts table so I just check if it's either the one or the other).If you don't have that post in your local database, it's a no-op because it's asking you to delete something you've never seen to begin with.
Delete
s already work like that. -
grishka:
Well then for me,
Remove{Note}
would simply be a fancier way for moderators to do the same thing thatDelete{Note}
would do.This is exactly what Lemmy does. Delete for your own posts, Remove for mod actions.
-
I think in order to reconcile FEP-400e and FEP-1b12 the following changes can be made:
- Both FEP-400e and FEP-1b12 implementations: support FEP-7888 (context collection)
- FEP-400e implementations: upgrade to Conversation Containers
- FEP-1b12 implementations: add
target
property toAnnounce
activity that points to context collection.
Activity types will still be different (
Add
vsAnnounce
), but consuming implementations can easily accept both. -
@[email protected] Unless I'm reading this wrong, you're advocating that we should:
- Reduce the number of moving parts to 2 (400e bows out in favour of 171b)
- Encourage broader adoption of a resolvable
context
property (this would be the one differentiator that can be used as feature detection) - Implementations should accept both
Add
andAnnounce
activities as valid for appending to a resolvable context collection.
Let me know if I have that right, I'd like to discuss this at the upcoming ForumWG meeting Thursday.
-
julian:
Unless I'm reading this wrong, you're advocating that we should:
Yes, that's correct. I believe it is also important to make
context
a collection of activities, because reactions are part of a conversation too (I didn't fully realize that until I added support for FEP-1b12Announce(Like)
andAnnounce(Dislike)
) -
disagree that
context
should contain likes and dislikes. i think those should go in collections likelikes
or an extension-defineddislikes
.but also,
context
is agnostic to what it contains. this is why i was thinking of maybe defining new types to signal that the collection should contain "posts" (objects that havecontent
) only, or if it can contain other non-"post" objects like Like/Announce/etc activities.the main mechanism for deciding what goes in a context collection is more or less "whatever declares that
context
and gets delivered to / added by the authority" -
Okay, so we have some disagreement here with respect to what a
context
contains. Perhaps my understanding was outdated, but I thought there was some limited discussion aboutoutbox
containing the full activity log, and the collection containing the post objects. That would satisfy both sides, if I am not mistaken.Usually the conversation devolves into "what is an actor, anyway", but let's keep things on track today LOL
-
trwnh:
disagree that
context
should contain likes and dislikes. i think those should go in collections likelikes
or an extension-defineddislikes
.So servers will need to make additional requests for every object in a context collection when backfilling a thread? This is quite inefficient.
The goal is 0 additional requests because every activity in collection is signed.
-
i am in favor of having contexts contain "post" objects as you well know, and for making collections into actors that can be followed and can have outboxes. you're not outdated or mistaken there
the only missing bit is some signal to describe what a context collection contains, and if it is a collection-actor, then some signal to describe how it behaves.
specifically this where the new types could come in. very loosely i was toying with something like the following
for collections representing a conversation, we have the following attributes we could signal:
- "this collection contains objects sharing the same
context
" - "this collection contains objects that have text
content
"
for actors representing a topic or community, we have the following behaviors we could signal:
- "this actor announces any activity sent to it" (new style boost bots like 1b12)
- "this actor announces the object of any Create sent to it" (old style boost bots like guppe)
- "this actor adds any Create.object or activity it encounters with the same
context
to a context collection that it owns" (context manager)
maybe types aren't the best way to signal all of these things, but it's what we have available to us at the moment, barring the development of some kind of FEP to allow actors to signal which clients they have attached to them and how those clients behave.
at the very least, the collection subtypes make enough sense to consider for now... i might have to revisit the actor behaviors later, because if we just define types for every behavior then this could quickly lead to actors having more types than you know what to do with. also i think more precisely these behaviors are properties of the client attached to the actor, not the actor itself.
tldr if you want to distribute Likes and Dislikes and Announces as part of a context/conversation then i think the best we can do right now is have the author of the Like/Dislike/Announce consider putting
context
on those activities, and then it'd be up to the receiving context manager to decide whether they want to allow those contentless activities to be added to the context collection or not. - "this collection contains objects sharing the same
-
it's only inefficient if you care about like/dislike/share counts. i am fundamentally disagreeing that those activities should be considered part of a conversation. it adds nothing to the conversation to just tell everyone "i like this" with zero additional input to the conversation. now, if you wanted to clarify and say "i like this because ..." then maybe you actually are contributing to the conversation. you'd do that by putting
content
on your Like, which turns it into a "post". (posts dont have to be Notes or Articles. activities can be considered "posts" if they havecontent
.) -
From an implementor point of view, I will say that if I receive a context and it contains the entire activity history, I will simply retrieve everything and normalize it down to the items that I do care about.
For example, a collection containing:
Create(Note), Like(Note), Announce(Note), EmojiReact(Note), Create(Note)
would be collapsed down to
Note, Like, Announce, (ignored), Note
Whereas a collection containing just the objects would not need collapsing (it'd just be
Note, Note
). Either way, it containing the entire history does not impede my ability to parse it.Sure, I'd miss some of the activities around it, so that's a concern, but it's not a dealbreaker. That's why I'm on the fence about this one.