So, I tried to rewrite the original proposal so it is more focused on unbounded groups. Let me hear your thoughts . In the meantime, recent discussion in GNU social IRC has rewind the idea of automated group move to the one with more local followers when the previous one becomes unreachable for a certain period of time. I still prefer the one presented below as I don’t think it has as many corner cases and shortcomes, but included this concept in the summary and think it is very worthy of raising in discussion.
FEP-b615: Unbounded Actor
Summary
Historically, after the sudden death of a popular instance, one could neither target groups hosted at it anymore nor contact the whole followers collection to let them know of the new instance housing a certain group. If we always have absolute knowledge of the complete followers collection (or good enough), we can automate based on which instance has more local followers which server would become the new house. Another alternative would be to automatically archive the old group and start again from scratch.
This FEP, on the other hand, discusses something very different of automatically moving an actor from one server to a different one. It is about collaboration between different group actors to promote an unified experience between the participants of the linked group actors. We think this may be easier, flexible, and promotes a better UX than only notifying the actor that the house of a certain group has moved, but both solutions would probably achieve similar results in the above use case.
This proposal introduces two concepts: Group Link Relationship
, and gs:unbound
. The first makes two groups “act as one”, the second triggers a mechanism that attempts to augment that collection of linkedGroups
in an opportunistic manner.
This aims at effectively removing a central point of authority for groups, which is a probably useful functionality. With this, @alice@undefinedhackers.net
can mention a group named hackers (!hackers) or even address an activity To !hackers@social.undefinedhackers.net
(C2S) and let her instance’s !hackers announce to other instances’ !hackers.
Finally, this proposal is general enough to allow a server to simultaneously have !lug@server
(without links), !lug-unbounded@server
(with the greatest links collection it can grow), and !lug-with-some-links@server
(with only some links). It doesn’t require linked groups to have the same preferredUsername
.
Notation and Definitions
To keep things simple, sometimes you will see things formatted like Activity{Object}
. For example, Create{Note}
would be a Create
activity containing a Note
in the object field.
-
@nickname@server
will be used to refer Actors of type Person or Application.
-
!nickname@server
will be used to refer Actors of type Group or Organization.
-
@#!group@server#collection
will be used to refer collection collection
of !group@server
.
Other FEP Dependencies
ActivityStreams 2.0 requirements for this mechanism
Example Group Actor in this FEP
{
"id": "https://social.undefinedhackers.net/group/hackers",
"type": "Group",
"gs:unbound": true,
"preferredUsername": "hackers",
"linkedGroups": "https://social.undefinedhackers.net/group/hackers/relationships",
"endpoints": {
"sharedInbox": "https://social.undefinedhackers.net/inbox.json"
},
"inbox": "https://social.undefinedhackers.net/group/hackers/inbox.json",
"outbox": "https://social.undefinedhackers.net/group/hackers/outbox.json",
"publicKey": {
"id": "https://social.undefinedhackers.net/group/hackers#public-key",
"owner": "https://social.undefinedhackers.net/group/hackers",
"publicKeyPem": "..."
}
}
The linkedGroups
attribute points to an OrderedCollection
of URIs of actors of type Group in other servers that this group has established a “link” agent relationship with.
This attribute MUST be present in the actor if it wants to take advantage of the extension described in this document.
{
"type": "OrderedCollection",
"id": "https://social.undefinedhackers.net/group/hackers/relationships/links",
"orderedItems": [
"https://gnusocial.net/group/hackers",
"https://social.diogo.site/group/hackers",
"https://social.eliseuama.ro/group/hackers",
"https://social.hsal.es/group/hackers",
"https://status.hackerposse.com/group/hackers"
]
}
The collection is ordered lexicographically, making it faster to compare and merge with other collections.
Example of a Group Link relationship
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://social.undefinedhackers.net/group/lug/relationships/links/1",
"summary": "!lug@social.undefinedhackers.net is linked with !lug@gnusocial.net",
"type": "Relationship",
"subject": {
"type": "Group",
"id": "https://social.undefinedhackers.net/group/lug",
"role": "link"
},
"relationship": "https://w3id.org/#AgentRelationship",
"object": {
"type": "Group",
"id": "https://gnusocial.net/group/lug",
"role": "link"
},
"target": {
"type": "Collection",
"id": "https://social.undefinedhackers.net/group/lug/relationships/links",
"attributedTo": "https://social.undefinedhackers.net/group/lug"
}
}
Link request using FEP-400e
{
"@context": "https://www.w3.org/ns/activitystreams",
"summary": "!lug@social.undefinedhackers.net wants to link with !lug@gnusocial.net",
"actor": "https://social.undefinedhackers.net/group/hackers",
"id": "https://social.undefinedhackers.net/activity/1337",
"to": "https://www.w3.org/ns/activitystreams#Public",
"type": "Create",
"object": "https://social.undefinedhackers.net/group/lug/relationships/links/1",
"target": "https://gnusocial.net/group/hackers/relationships"
}
The handling of the above activity happens as described in [FEP-400e], Section “Adding an object of a colection”.
Assuming that !hackers@social.undefinedhackers.net
and !lug@gnusocial.net
have mutually agreeded in adding each other to their links, that will result in !hackers@social.undefinedhackers.net
Announcing{*} entering its inbox to !lug@social.gnusocial.net
and vice versa. Allowing them to act as if they were the same group. If they have equivalent groupLinks
collections, then they are essentially fully mirrored groups.
Note that the “Linking negotiation” happens between two Group actors (S2S).
Example Announce{Note} from a Group and targeted at another Group (S2S)
{
"@context": [
"https://www.w3.org/ns/activitystreams"
],
"id": "https://social.undefinedhackers.net/activity/1337",
"type": "Announce",
"actor": "https://social.undefinedhackers.net/group/hackers",
"published": "2021-12-29T13:37:42Z",
"to": "https://social.undefinedhackers.net/group/hackers/relationships/links",
"cc": [
"https://www.w3.org/ns/activitystreams#Public"
],
"object": {
"id": "https://social.hackersatporto.com/object/note/31337",
"type": "Note",
"published": "2021-12-29T13:37:42Z",
"attributedTo": "https://social.undefinedhackers.net/person/1",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://social.hackersatporto.com/index.php/user/1"
],
"content": "<p>hello, <span class=\"h-card\"><a href=\"https://social.undefinedhackers.net/!hackers\" class=\"u-url mention\">!<span>hackers</span></a></span></p>",
"tag": [
{
"type": "Group",
"name": "!hackers",
"href": "https://social.undefinedhackers.net/!hackers"
}
]
},
"signature": {
"type": "RsaSignature2017",
"creator": "https://social.undefinedhackers.net/group/hackers#public-key",
"created": "2021-12-29T13:37:42Z",
"signatureValue": "..."
}
}
}
The local persons interested in interacting with an unbounded group
MAY mention it using !group
without the @server
bit, the local server in that situation can assume !group@server
. !group@server
will then reproduce the above Announce activity.
Opportunistic linkedGroups
Augmentation Mechanism (when gs:unbound
is true)
Let’s suppose that the Announce above has just been delivered to https://gnusocial.net/inbox.json
.
And let’s assume the following items in !hackers@gnusocial.net
's linkedGroups
collection:
"orderedItems": [
"https://loadaverage.org/group/hackers",
"https://social.diogo.site/group/hackers",
"https://social.undefinedhackers.net/group/hackers"
]
gnusocial.net
must include the unknown addressed to
actors in its own linkedGroups
collection (excluding itself), so that it becomes:
"orderedItems": [
"https://loadaverage.org/group/hackers",
"https://social.diogo.site/group/hackers",
"https://social.eliseuama.ro/group/hackers",
"https://social.hsal.es/group/hackers",
"https://social.undefinedhackers.net/group/hackers",
"https://status.hackerposse.com/group/hackers"
]
For that it should use the described in section [Link request using FEP-400e] for each new group.
Obs.: If in the addressed to
groups there’s one that gnusocial.net
knows to be dead, the dead actor should be in a temporary list for sufficient time to avoid re-adding it to the connections.
Obs.: This only happened because the actor had the gs:unbound
attribute set to true.
Obs.: gnusocial.net
should record groups that have previously rejected a Link request.
Examples
Please note that the following examples are not independent, each continuously builds on the previous one.
1. Creating a group with a link
-
!hackers@
A: Create{Group}
@#!hackers@
A#linkedGroups
= []
-
!hackers@
B: Create{Group} (S2S)
-
!hackers@
B: Create{Relationship} TO !hackers@
A (S2S)
!hackers@
B#linkedGroups
= [A]
-
!hackers@
A: Create{Relationship} TO !hackers@
B (S2S)
!hackers@
A#linkedGroups
= [B]
-
@alice@
B: Create{Note} TO !hackers@
B (C2S)
-
!hackers@
B: Announce{Note} TO !hackers@
[A] (S2S)
@#!hackers@
A#linkedGroups
is unchanged as he was the only in TO
2. Opportunistic linkedGroups
Augmentation Mechanism
-
!hackers@
C: Create{Group}
-
!hackers@
C: Create{Relationship} TO !hackers@
B
@#!hackers@
C#linkedGroups
= [B]
-
!hackers@
B: Create{Relationship} TO !hackers@
C
@#!hackers@
B#linkedGroups
= [A, C]
-
@bob@
C: Create{Note} TO [@tux@
D (S2S), !hackers@
C (C2S)]
Let’s assume that D either doesn’t have the group !hackers or has the group but @#!hackers@
D#linkedGroups
= []
@#!hackers@
D#linkedGroups
= [B]
-
!hackers@
B: Create{Relationship} TO !hackers@
D (S2S)
@#!hackers@
B#linkedGroups
= [A, C, D]
3. Forwarding from Inbox
-
!hackers@
C: Announce{Note} TO !hackers@
[B] (S2S)
-
B will be kind enough to
inboxForward
this activity TO A
-
A fetches
!hackers@
C and merges the existing linkedGroups
with !hackers@
C’s linkedGroups
-
!hackers@
A: Create{Relationship} TO !hackers@
C (S2S)
@#!hackers@
A#linkedGroups
= [B, C], was updated due to the proposed augumentation mechanism
-
!hackers@
C: Create{Relationship} TO !hackers@
A (S2S)
@#!hackers@
C#linkedGroups
= [A, B]
This is not the same as the defined in [ActivityPub], Section 7.1.2. As it does not respect bullet points 2 and 3. It instead acts on the premise that there are values in to
that the sender doesn’t yet know about but would like to, this is a safe premise when the gs:unbound
attribute is set to true.
N.B.: Unless the linkedGroups in the To
are exactly the same as the ones the receiving instance already has, a diff will always have to be made. Therefore, noticing that some of ours linkedGroups aren’t going to be notified is a free by-product of augumenting our current linkedGroups.