#267 RFC: Implementing Groups in v3

Open
opened 2 years ago by diogo · 2 comments

Discussion moved to: https://socialhub.activitypub.rocks/t/decentralised-group/2200


authors: Diogo Peralta Cordeiro mail@diogo.site thanks: Hugo Sales hugo@hsal.es, Joshua Judson Rosen rozzin.gnusocial@hackersposse.com, Eliseu Amaro mail@eliseuama.ro status: DRAFT

dateReceived: 2021-12-30

FEP-2bca: Decentralised Group

Summary

After the death of popular instances such as Qvitter, one could neither target groups hosted at Qvitter anymore nor contact the whole subscribers' list to let them know of the new instance hosting a certain group.

This proposal introduces the concept of umbrella groups. In these, @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.

Every instance is responsible for maintaining both a list of local members and known instances holding the same group. The augmentation mechanism for the latter is described in this document.

This aims at effectively removing a central point of authority for groups, which is something desired at GNU social. For a more featureful group with administrators and moderators, we would instead use the organisation actor type.

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.

GNU social mentioning notation:

Object type Description Mention prefix
Application A machine that acts similar to a NPC @bot@instance (@instance can be omitted for locals)
Group Collection of persons without an authority !group (@instance cannot be included, and wouldn't make sense)
Organisation Collection of persons with an authority !org@instance (@instance cannot be omitted, even for locals)
Person Human actor @person@instance (@instance can be omitted for locals)
Service Content-producing service no mentioning
Collection of Actors Collection of Actors @#circle@instance (@instance can be omitted for locals)
HashTag A tag in a note #note.tag

WebFinger

Servers cannot allow applications and persons to have the same nickname, due to WebFinger discovery, these two share the same acct namespace.

WebFinger discovery won't be used for any of the other described Object types. For example, following an !org@instance requires subscribing to it in instance's page.

ActivityStreams 2.0 requirements for this mechanism

The Actor Group

{
  "id": "https://social.undefinedhackers.net/group/hackers",
  "type": "Group",
  "gs:umbrella": true,
  "preferredUsername": "hackers",
  "connections": "https://social.undefinedhackers.net/group/hackers/connections",
  "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 Connections Collection

The connections attribute points to a OrderedCollection of URIs of actors of type Group in other instances with equal preferredUsername. It looks like this:

{
  "type": "OrderedCollection",
  "id": "https://social.undefinedhackers.net/group/hackers/connections",
  "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 of merging with other collections.

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://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"
    ],
    "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": "..."
    }
  }
}

N.B.: The to attribute is the !hackers@social.undefinedhackers.net's connections collection's content.

Connections augmentation mechanism

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 connections 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 connections collection (excluding himself), 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"
  ]

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:umbrella attribute set to true.

Examples

Please note that the following examples are not independent, each continuously builds on the previous one.

1. Creating a group with a pre-connection (basic)

  • !hackers@A: Create/Group

@#!hackers@A#connections = []

  • !hackers@B: Create/Group TO !hackers@A (S2S)

!hackers@B#connections = [A]

!hackers@A#connections = [B]

  • @alice@B: Create/Note TO !hackers@B (C2S)
  • !hackers@B: Announce/Note TO !hackers@[A] (S2S)

@#!hackers@A#connections is unchanged as he was the only in TO

2. Augmentation Mechanism (regular)

  • !hackers@C: Create/Group TO !hackers@B

@#!hackers@C#connections = [B]

@#!hackers@B#connections = [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#connections = []

  • D fetches !hackers@C and merges the existing connections (none) with !hackers@C's connections

@#!hackers@D#connections = [B]

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 connections with !hackers@C's connections

@#!hackers@A#connections = [B, C], was updated due to the proposed augumentation mechanism

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:umbrella attribute is set to true.

N.B.: Unless the connections 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 connections aren't going to be notified is a free by-product of augumenting our current connections.

Copyright

CC0 1.0 Universal (CC0 1.0) Public Domain Dedication

To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.

Discussion moved to: https://socialhub.activitypub.rocks/t/decentralised-group/2200 --- authors: Diogo Peralta Cordeiro <mail@diogo.site> thanks: Hugo Sales <hugo@hsal.es>, Joshua Judson Rosen <rozzin.gnusocial@hackersposse.com>, Eliseu Amaro <mail@eliseuama.ro> status: DRAFT dateReceived: 2021-12-30 --- # FEP-2bca: Decentralised Group ## Summary After the death of popular instances such as Qvitter, one could neither target groups hosted at Qvitter anymore nor contact the whole subscribers' list to let them know of the new instance hosting a certain group. This proposal introduces the concept of umbrella groups. In these, `@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. Every instance is responsible for maintaining both a list of local members and known instances holding the same group. The augmentation mechanism for the latter is described in this document. This aims at effectively removing a central point of authority for groups, which is something desired at GNU social. For a more featureful group with administrators and moderators, we would instead use the organisation actor type. ## 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. ### GNU social mentioning notation: Object type | Description | Mention prefix ---------------------|------------------------------------------------|--------------- Application | A machine that acts similar to a NPC | @bot@instance (@instance can be omitted for locals) Group | Collection of persons **without** an authority | !group (@instance cannot be included, and wouldn't make sense) Organisation | Collection of persons **with** an authority | !org@instance (@instance cannot be omitted, even for locals) Person | Human actor | @person@instance (@instance can be omitted for locals) Service | Content-producing service | no mentioning Collection of Actors | Collection of Actors | @#circle@instance (@instance can be omitted for locals) HashTag | A tag in a note | #note.tag ### WebFinger Servers cannot allow applications and persons to have the same nickname, due to WebFinger discovery, these two share the same `acct` namespace. WebFinger discovery won't be used for any of the other described Object types. For example, following an `!org@instance` requires subscribing to it in instance's page. ## ActivityStreams 2.0 requirements for this mechanism ### The Actor Group ```json { "id": "https://social.undefinedhackers.net/group/hackers", "type": "Group", "gs:umbrella": true, "preferredUsername": "hackers", "connections": "https://social.undefinedhackers.net/group/hackers/connections", "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 Connections Collection The `connections` attribute points to a `OrderedCollection` of URIs of actors of type Group in other instances with equal `preferredUsername`. It looks like this: ```json { "type": "OrderedCollection", "id": "https://social.undefinedhackers.net/group/hackers/connections", "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 of merging with other collections. ### Announce/Note from a Group and targeted at another Group (S2S) ```json { "@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://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" ], "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": "..." } } } ``` **N.B.:** The `to` attribute is the `!hackers@social.undefinedhackers.net`'s `connections` collection's content. ### Connections augmentation mechanism 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 connections collection: ```json "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 connections collection (excluding himself), so that it becomes: ```json "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" ] ``` _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:umbrella` attribute set to true. ## Examples Please note that the following examples are not independent, each continuously builds on the previous one. ### 1. Creating a group with a pre-connection (basic) - `!hackers@`**A**: Create/Group > `@#!hackers@`**A**`#connections` = **[]** - `!hackers@`**B**: Create/Group _TO_ `!hackers@`**A** (S2S) > `!hackers@`**B**`#connections` = **[A]** > `!hackers@`**A**`#connections` = **[B]** - `@alice@`**B**: Create/Note _TO_ `!hackers@`**B** (C2S) - `!hackers@`**B**: Announce/Note _TO_ `!hackers@`**[A]** (S2S) > `@#!hackers@`**A**`#connections` is unchanged as he was the only in _TO_ ### 2. Augmentation Mechanism (regular) - `!hackers@`**C**: Create/Group _TO_ `!hackers@`**B** > `@#!hackers@`**C**`#connections` = **[B]** > `@#!hackers@`**B**`#connections` = **[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**`#connections` = **[]** - **D** fetches `!hackers@`**C** and merges the existing connections (none) with `!hackers@`**C**'s connections > `@#!hackers@`**D**`#connections` = **[B]** ### 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 connections with `!hackers@`**C**'s connections > `@#!hackers@`**A**`#connections` = [B, C], was updated due to the proposed augumentation mechanism This is not the same as the defined in [ActivityPub], [Section 7.1.2](https://www.w3.org/TR/activitypub/#inbox-forwarding). 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:umbrella` attribute is set to true. **N.B.:** Unless the connections 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 connections aren't going to be notified is a free by-product of augumenting our current connections. ## Copyright CC0 1.0 Universal (CC0 1.0) Public Domain Dedication To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.
Diogo Cordeiro commented 2 years ago
Owner

Revisiting V2 groups

WebFinger

https://gnusocial.net/.well-known/webfinger?resource=https://gnusocial.net/group/892/id

{
  "subject": "https://gnusocial.net/group/892/id",
  "aliases": [
    "https://gnusocial.net/group/testgs",
    "https://gnusocial.net/index.php/group/892/id",
    "https://gnusocial.net/index.php/group/testgs"
  ],
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://gnusocial.net/group/testgs"
    },
    {
      "rel": "http://gmpg.org/xfn/11",
      "type": "text/html",
      "href": "https://gnusocial.net/group/testgs"
    },
    {
      "rel": "describedby",
      "type": "application/rdf+xml",
      "href": "https://gnusocial.net/group/testgs/foaf"
    },
    {
      "rel": "http://schemas.google.com/g/2010#updates-from",
      "type": "application/atom+xml",
      "href": "https://gnusocial.net/api/statusnet/groups/timeline/892.atom"
    },
    {
      "rel": "salmon",
      "href": "https://gnusocial.net/main/salmon/group/195563"
    },
    {
      "rel": "http://salmon-protocol.org/ns/salmon-replies",
      "href": "https://gnusocial.net/main/salmon/group/195563"
    },
    {
      "rel": "http://salmon-protocol.org/ns/salmon-mention",
      "href": "https://gnusocial.net/main/salmon/group/195563"
    },
    {
      "rel": "http://specs.openid.net/auth/2.0/provider",
      "href": "https://gnusocial.net/group/testgs"
    },
    {
      "rel": "http://ostatus.org/schema/1.0/subscribe",
      "template": "https://gnusocial.net/main/remotefollowsub?profile={uri}"
    }
  ]
}

It doesn't have an acct:testgs@gnusocial.net alias

When someone !mentions a group, OStatus plugin checks for locally known groups in order to mention it. I.e., it works as long as someone has already subscribed a group.

Actor Group attributes

  • Nickname (different namespace from Person nicknames)
  • Full name
  • Homepage: A URL
  • Description
  • Location: City, State (or Region), Country
  • Aliases: Extra nicknames for the group, separated with commas or spaces. Maximum 3 aliases allowed.
  • Private: bool; New members must be approved by admin and all posts are forced to be private.
  • Private messages: Somtimes, Always, Never; Whether to allow private messages to this group.
  • Private senders: Everyone, Member, Admin

Functionality

  • One can Post To !group(@instance)
  • One can Subscribe a group (and afterwards Leave it)
  • One can mention !group(@instance)
# Revisiting V2 groups ## WebFinger https://gnusocial.net/.well-known/webfinger?resource=https://gnusocial.net/group/892/id ```json { "subject": "https://gnusocial.net/group/892/id", "aliases": [ "https://gnusocial.net/group/testgs", "https://gnusocial.net/index.php/group/892/id", "https://gnusocial.net/index.php/group/testgs" ], "links": [ { "rel": "http://webfinger.net/rel/profile-page", "type": "text/html", "href": "https://gnusocial.net/group/testgs" }, { "rel": "http://gmpg.org/xfn/11", "type": "text/html", "href": "https://gnusocial.net/group/testgs" }, { "rel": "describedby", "type": "application/rdf+xml", "href": "https://gnusocial.net/group/testgs/foaf" }, { "rel": "http://schemas.google.com/g/2010#updates-from", "type": "application/atom+xml", "href": "https://gnusocial.net/api/statusnet/groups/timeline/892.atom" }, { "rel": "salmon", "href": "https://gnusocial.net/main/salmon/group/195563" }, { "rel": "http://salmon-protocol.org/ns/salmon-replies", "href": "https://gnusocial.net/main/salmon/group/195563" }, { "rel": "http://salmon-protocol.org/ns/salmon-mention", "href": "https://gnusocial.net/main/salmon/group/195563" }, { "rel": "http://specs.openid.net/auth/2.0/provider", "href": "https://gnusocial.net/group/testgs" }, { "rel": "http://ostatus.org/schema/1.0/subscribe", "template": "https://gnusocial.net/main/remotefollowsub?profile={uri}" } ] } ``` It doesn't have an acct:testgs@gnusocial.net alias When someone !mentions a group, OStatus plugin checks for locally known groups in order to mention it. I.e., it works as long as someone has already subscribed a group. ## Actor Group attributes * Nickname (different namespace from Person nicknames) * Full name * Homepage: A URL * Description * Location: City, State (or Region), Country * Aliases: Extra nicknames for the group, separated with commas or spaces. Maximum 3 aliases allowed. * Private: bool; New members must be approved by admin and all posts are forced to be private. * Private messages: Somtimes, Always, Never; Whether to allow private messages to this group. * Private senders: Everyone, Member, Admin ## Functionality * One can Post `To` !group(@instance) * One can Subscribe a group (and afterwards Leave it) * One can mention !group(@instance)
Diogo Cordeiro commented 2 years ago
Owner

Functionality in GNU social v3 groups

The @actor@localhost can:

  • Create/Note To !group@localhost (C2S)
  • Follow a !group@localhost (and afterwards undo it) (C2S)
  • Create/Note mention !group
  • Choose what connections to listen to in a !group
  • Add connections to a !group's connections collection

History of drafts before the current version on first post: #186

## Functionality in GNU social v3 groups The `@actor@localhost` can: * Create/Note To `!group@localhost` (C2S) * Follow a `!group@localhost` (and afterwards undo it) (C2S) * Create/Note mention !group * Choose what connections to listen to in a !group * Add connections to a !group's connections collection History of drafts before the current version on first post: https://notabug.org/diogo/gnu-social/issues/186
Sign in to join this conversation.
No Milestone
No assignee
1 Participants
Loading...
Cancel
Save
There is no content yet.