# Groups Extension

Organizes users and notes into communities.

Extension name: `pub.versia:groups`

Groups are a way to organize users and notes into communities, for any purpose, such as forums, blogs or messaging.

This extension requires [`pub.versia:social@1`](/extensions/social), which defines [Notes](/extensions/social#note).

## Definition

### Group

Only the instance hosting a group may send or serve that Group.

- Type: `"pub.versia:groups/Group"`

- Fetch: `GET /.versia/v0.7/entities/pub.versia:groups/Group/{id}`

- Collections:

  - `members`: [Reference Collection](/structures/collection#reference-collection) of [Users](/entities/user) that are members of the group.
  - `notes`: [Reference Collection](/structures/collection#reference-collection) of [Notes](/extensions/social#note) that are associated with the group.
  - `moderators`: [Reference Collection](/structures/collection#reference-collection) of [Users](/entities/user) that moderate the group. It may be empty. This is for display and contact only: the instance hosting the group enforces its rules.

**Fields**

- `type` (`string`, required): **MUST** be `pub.versia:groups/Group`.

- `name` (`string`): Group name or title, as plain text.

- `description` (`RichText`): Short description of the group's contents and purpose.

- `open` (`boolean`, required): Whether the group is open to all users or requires approval to join.

  > **This is meant as a UI hint** and does not guarantee that group subscriptions will be accepted or rejected.
  >
  > It is similar to a [User](/entities/user)'s `manually_approves_subscribers` field.

- `url` (`URI`): Address of the group's page on its instance, for people to open in a browser. Software that shows the group elsewhere can link to it.

Inherited from [Entity](/entities):

- [`id`](/entities#entity-id) (`string`, required)
- [`created_at`](/entities#entity-created-at) (`RFC3339`, required)
- [`updated_at`](/entities#entity-updated-at) (`RFC3339`)
- [`extensions`](/entities#entity-extensions) (`Extensions`)

```jsonc title="Example Group"
{
    "type": "pub.versia:groups/Group",
    "id": "033j6qrwzt002",
    "created_at": "2021-01-01T00:00:00Z",
    "name": "The Woozy fan club",
    "description": "<p>A group for fans of the <strong>Woozy</strong> emoji.</p>",
    "open": false,
    "url": "https://example.com/groups/woozy"
}
```

### Notes in a Group

A [Note](/extensions/social#note) is posted to a group through the `pub.versia:groups` key of its `extensions` field.

Members of the group can see the Note, on top of the audience its `visibility` gives it, and their instances [may fetch it](/federation#access). A Note with a `visibility` of `mentions` can be seen only by members and the rest of its `mentions` audience. An instance that does not support this extension shows it only to the mentioned users.

- Extension ID: `pub.versia:groups`
- Extends: [Note](/entities/note)

**Fields**

- `group` (`Reference`, required): [Reference](/entities#reference) to the [Group](#group) the Note is posted to.

```jsonc title="Example Note in a Group"
{
    "type": "pub.versia:social/Note",
    "visibility": "mentions",
    // ...
    "extensions": {
        "pub.versia:groups": {
            "group": "example.com:033j6qrwzt002"
        }
    }
}
```

### GroupFederate

The `GroupFederate` entity tells an instance to fetch a Note posted to a group. **MUST** be sent by the instance hosting the group.

A receiver **MUST** ignore a `GroupFederate` that does not come from the instance hosting `group`, or whose fetched Note does not name that `group` in its `pub.versia:groups` extension.

- Type: `"pub.versia:groups/Federate"`
- Lifetime: [Transient Entity](/entities#transient-entities)

**Fields**

- `type` (`string`, required): **MUST** be `pub.versia:groups/Federate`.
- `target` (`Reference`, required): [Reference](/entities#reference) to the [Note](/extensions/social#note) to fetch.
- `group` (`Reference`, required): [Reference](/entities#reference) to the group federating the note.

Inherited from [Entity](/entities):

- [`created_at`](/entities#entity-created-at) (`RFC3339`, required)
- [`extensions`](/entities#entity-extensions) (`Extensions`)

```jsonc title="Example GroupFederate"
{
    "type": "pub.versia:groups/Federate",
    "target": "versia.example:034e17jw54002",
    "group": "example.com:033j6qrwzt002",
    "created_at": "2021-01-01T00:00:00Z"
}
```

## Behavior

### Subscribing to Groups

Groups accept [subscriptions](/entities/subscribe). A [User](/entities/user) joins a group by sending a [Subscribe](/entities/subscribe) whose `target_type` is `pub.versia:groups/Group`, and leaves it with an [Unsubscribe](/entities/unsubscribe). The group's instance answers as for any subscription, including when the group is `open`, and a [SubscribeReject](/entities/subscribe-reject) also removes a member.

A Group's subscribers are its **members**, listed in its `members` collection. They receive the Notes posted to it, as described under [Federation](#federation).

### Federation

The instance hosting a group cannot send the Notes posted to it, as an entity's author [must belong to the instance that signs it](/validation#authors-and-signers). A Note therefore reaches the group's members in three steps:

1. The instance of the Note's author **MUST** send the [Note](/extensions/social#note) to the [inbox](/federation/endpoints#inbox) of the group's instance, on top of its usual [delivery](/federation#delivery). It **MUST** do the same with any change to the Note, and with a [Delete](/entities/delete) for it.
2. The group's instance decides which Notes to federate. A Note it refuses is dropped silently. For the others, it **MUST** send a `GroupFederate` to every instance hosting a member of the group.
3. Each of those instances fetches the Note from the instance of its author.

The instance of the author **MUST** allow that fetch if the requesting instance hosts a user in the group's `members` collection, unless it [refuses to federate](/federation/http#status-codes) with that instance. It **MAY** cache that collection, and **SHOULD** fetch it again before refusing an instance it does not find there.

When a Note posted to a group changes or is deleted, the group's instance **MUST** send a `GroupFederate` for it again, and receivers fetch it again. The instance of the author answers a request for the deleted Note as [Delete describes](/entities/delete#fetching-deleted-entities). After a `404 Not Found`, members keep their copy.

## Delivery and Access

| Entity                                                        | Delivered to                            |
| ------------------------------------------------------------- | --------------------------------------- |
| `GroupFederate`                                               | Instances hosting a member of the group |
| [Group](#group), after a change or [Delete](/entities/delete) | Instances hosting a member of the group |

Any instance may fetch a Group and its `members` and `moderators` collections.

## Versia Links

The Groups extension adds the following [Versia Link](/links) action:

- `groups/{instance}/{id}`: Open a group.
