mirror of
https://github.com/versia-pub/docs.git
synced 2025-12-06 06:18:19 +01:00
refactor: 📝 Update all extension to remove now-useless fields
This commit is contained in:
parent
51c53824ad
commit
d886b83e62
|
|
@ -17,12 +17,13 @@ Having the authorization is defined as:
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Delete">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="author" type="URI | null" required={true} typeLink="/types#uri">
|
||||
URI of the `User` who is deleting the entity. [Can be set to `null` to represent the instance](/entities/instance-metadata#the-null-author).
|
||||
</Property>
|
||||
|
|
@ -43,6 +44,7 @@ Having the authorization is defined as:
|
|||
"id": "9b3212b8-529c-435a-8798-09ebbc17ca74",
|
||||
"created_at": "2021-01-01T00:00:00.000Z",
|
||||
"author": "6e0204a2-746c-4972-8602-c4f37fc63bbe",
|
||||
"deleted_type": "Note",
|
||||
"deleted": "02e1e3b2-cb1f-4e4a-b82e-98866bee5de7"
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ export const metadata = {
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="FollowAccept">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="author" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the `User` considered the 'followee', i.e. the user who is being followed.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@ But it can also be used when Bob is already following Alice, in the case that:
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="FollowReject">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="author" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the `User` considered the 'followee', i.e. the user who is being followed.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -53,12 +53,13 @@ Once a follow relationship is established, the **followee**'s instance should se
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Follow">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="author" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the `User` considered the 'follower'.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Contains metadata about a Versia instance, such as capabilities and endpoints. {
|
|||
|
||||
## The `null` Author
|
||||
|
||||
On some entities that have an `author` field, the `author` can be `null` to represent the instance itself as the author (like ActivityPub's Server Actors). In this case, the instance's public key should be used to verify the entity. To know which instance's public key to use, the entity signature should be used.
|
||||
On some entities that have an `author` field, the `author` can be `null` to represent the instance itself as the author (like ActivityPub's Server Actors).
|
||||
|
||||
Check the entity's documentation page to see if it supports this (it will be noted in the `author` field).
|
||||
|
||||
|
|
@ -21,9 +21,6 @@ Check the entity's documentation page to see if it supports this (it will be not
|
|||
<Property name="id" type="null">
|
||||
This entity does not have an ID.
|
||||
</Property>
|
||||
<Property name="uri" type="null">
|
||||
This entity does not have a URI.
|
||||
</Property>
|
||||
<Property name="name" type="string" required={true}>
|
||||
Friendly name of the instance, for humans.
|
||||
</Property>
|
||||
|
|
@ -60,9 +57,6 @@ Check the entity's documentation page to see if it supports this (it will be not
|
|||
<Property name="host" type="string" required={true}>
|
||||
Hostname of the instance. Includes the port if it is not the default (i.e. `443` for HTTPS).
|
||||
</Property>
|
||||
<Property name="shared_inbox" type="URI" required={false}>
|
||||
URI to the instance's shared inbox, if supported.
|
||||
</Property>
|
||||
<Property name="public_key" type="PublicKey" required={true}>
|
||||
Public key of the instance.
|
||||
|
||||
|
|
@ -76,20 +70,6 @@ Check the entity's documentation page to see if it supports this (it will be not
|
|||
- `algorithm`: Algorithm used for the public key. Can only be `ed25519` for now.
|
||||
- `key`: Instance public key, in [SPKI-encoded base64](/signatures#exporting-the-public-key).
|
||||
</Property>
|
||||
<Property name="moderators" type="URI" required={false}>
|
||||
URI to [Collection](/structures/collection) of instance moderators.
|
||||
|
||||
<Note>
|
||||
This is for human consumption (such as moderator contact), not for any kind of protocol authorization.
|
||||
</Note>
|
||||
</Property>
|
||||
<Property name="admins" type="URI" required={false}>
|
||||
URI to [Collection](/structures/collection) of instance administrators.
|
||||
|
||||
<Note>
|
||||
This is for human consumption (such as admin contact), not for any kind of protocol authorization.
|
||||
</Note>
|
||||
</Property>
|
||||
<Property name="logo" type="ContentFormat" required={false} typeLink="/structures/content-format">
|
||||
Logo of the instance. Must be an image format (`image/*`).
|
||||
</Property>
|
||||
|
|
@ -145,3 +125,12 @@ Check the entity's documentation page to see if it supports this (it will be not
|
|||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
### Collections
|
||||
|
||||
The following [Collections](/structures/collection) are available:
|
||||
|
||||
- `moderators`: [Collection](/structures/collection) of instance moderators ([Users](/entities/user)).
|
||||
- `admins`: [Collection](/structures/collection) of instance administrators ([Users](/entities/user)).
|
||||
|
||||
These can be fetched using the [Federation API](/api/endpoints#entity-collections)
|
||||
|
|
@ -36,28 +36,6 @@ Notes represent a piece of content on a Versia instance. They can be posted by [
|
|||
| "messaging"; // Like Discord, Element (Matrix), Signal
|
||||
```
|
||||
</Property>
|
||||
<Property name="collections" type="NoteCollections" required={true}>
|
||||
Collections related to the note. Must contain at least `replies` and `quotes`.
|
||||
|
||||
```typescript
|
||||
type URI = string;
|
||||
|
||||
type NoteCollections = {
|
||||
replies: URI;
|
||||
quotes: URI;
|
||||
// Same format as type on Extensions
|
||||
[key: ExtensionsKey]: URI;
|
||||
}
|
||||
```
|
||||
|
||||
All URIs must resolve to either a [Collection](/structures/collection) or a [URI Collection](/structures/collection#uri-collection) of the appropriate entities. Extensions may add additional collections.
|
||||
|
||||
### Replies
|
||||
All replies to this note (have this note as their `replies_to`). [URI Collection](/structures/collection#uri-collection) of [Note](/entities/note) entities.
|
||||
|
||||
### Quotes
|
||||
All quotes of this note (have this note as their `quotes`). [URI Collection](/structures/collection#uri-collection) of [Note](/entities/note) entities.
|
||||
</Property>
|
||||
<Property name="content" type="ContentFormat" required={false} typeLink="/structures/content-format">
|
||||
The content of the note. Must be text format (`text/html`, `text/markdown`, etc). Must not be remote.
|
||||
</Property>
|
||||
|
|
@ -178,3 +156,12 @@ Notes represent a piece of content on a Versia instance. They can be posted by [
|
|||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
### Collections
|
||||
|
||||
The following [Collections](/structures/collection) are available:
|
||||
|
||||
- `replies`: [URI Collection](/structures/collection#uri-collection) of [Note](/entities/note) entities that are replies to this note.
|
||||
- `quotes`: [URI Collection](/structures/collection#uri-collection) of [Note](/entities/note) entities that quote this note.
|
||||
|
||||
These can be fetched using the [Federation API](/api/endpoints#entity-collections)
|
||||
|
|
@ -19,7 +19,12 @@ Any field in an entity not marked as `required` may be omitted or set to `null`.
|
|||
|
||||
<Properties name="Entity">
|
||||
<Property name="id" type="string" required={true}>
|
||||
Unique identifier for the entity. Must be unique within the instance. Can be any string. Max of 512 UTF-8 characters.
|
||||
Unique identifier for the entity. Must be unique within the instance. Can be any string with the following character sets:
|
||||
|
||||
- `a-z`
|
||||
- `A-Z`
|
||||
- `0-9`
|
||||
- `-`, `_`
|
||||
</Property>
|
||||
<Property name="type" type="string" required={true}>
|
||||
Type of the entity. Custom types must follow [Extension Naming](/extensions#naming).
|
||||
|
|
@ -31,13 +36,6 @@ Any field in an entity not marked as `required` may be omitted or set to `null`.
|
|||
Handling of dates that are valid but obviously incorrect (e.g. in the future) is left to the Implementation's discretion.
|
||||
</Note>
|
||||
</Property>
|
||||
<Property name="uri" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the entity. Should be unique and resolve to the entity. Must be an absolute URI.
|
||||
|
||||
<Note>
|
||||
[**Transient Entities**](/entities#transient-entities) do not require a URI.
|
||||
</Note>
|
||||
</Property>
|
||||
<Property name="$schema" type="string" required={false}>
|
||||
URL of any JSON Schema that the entity adheres to.
|
||||
|
||||
|
|
@ -86,7 +84,7 @@ Any field in an entity not marked as `required` may be omitted or set to `null`.
|
|||
|
||||
## Transient Entities
|
||||
|
||||
Some entities are transient, meaning they do not have a URI. These entities are used for actions that do not require a permanent record, such as deletions or migrations.
|
||||
Some entities are transient, meaning they cannot be refetched using the [Federation API](/api/endpoints). These entities are used for actions that do not require a permanent record, such as deletions or migrations.
|
||||
|
||||
Implementations **must not** rely on other implementations to store transient entities in their database.
|
||||
|
||||
|
|
@ -95,5 +93,5 @@ Implementations **must not** rely on other implementations to store transient en
|
|||
When serialized to a string, the JSON representation of an entity must follow the following rules:
|
||||
- Keys must be sorted lexicographically.
|
||||
- Must use UTF-8 encoding.
|
||||
- Must be **signed** using the relevant [User](/entities/user)'s private key, or the [instance's private key](/entities/instance-metadata) if the entity is not associated with a particular user.
|
||||
- Must be **signed** using the the [instance's private key](/entities/instance-metadata).
|
||||
- Must use Unix-style `\n` line endings (LF).
|
||||
|
|
@ -26,12 +26,13 @@ Sometimes, [Users](/entities/user) want to unsubscribe from each other to stop s
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Unfollow">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="author" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the `User` considered the 'follower', i.e. the user who is unsubscribing from the followee.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -70,64 +70,12 @@ Instance **must** be the host of the instance the user is on (hostname with opti
|
|||
<Property name="header" type="ContentFormat" required={false} typeLink="/structures/content-format">
|
||||
A header image for the user's profile. Also known as a cover photo or a banner. Must be an image format (`image/*`).
|
||||
</Property>
|
||||
<Property name="public_key" type="PublicKey" required={true}>
|
||||
The user's public key. Must follow the [Versia Public Key](/signatures) format. `actor` may be a URI to another user's profile, in which case this key may allow the other user act on behalf of this user (see [delegation](/federation/delegation)).
|
||||
|
||||
- `algorithm`: Must be `ed25519` for now.
|
||||
- `key`: The public key in [SPKI-encoded base64](/signatures#exporting-the-public-key). Must be the key associated with the `actor` URI.
|
||||
- `actor`: URI to a user's profile, most often the user's own profile.
|
||||
|
||||
```typescript
|
||||
type URI = string;
|
||||
|
||||
type PublicKey = {
|
||||
actor: URI;
|
||||
algorithm: string;
|
||||
key: string;
|
||||
}
|
||||
```
|
||||
</Property>
|
||||
<Property name="manually_approves_followers" type="boolean" required={false}>
|
||||
If `true`, the user must approve any new followers manually. If `false`, followers are automatically approved. This does not affect federation, and is meant to be used for clients to display correct UI. Defaults to `false`.
|
||||
</Property>
|
||||
<Property name="indexable" type="boolean" required={false}>
|
||||
User consent to be indexed by search engines. If `false`, the user's profile should not be indexed. Defaults to `true`.
|
||||
</Property>
|
||||
<Property name="inbox" type="URI" required={true} typeLink="/types#uri">
|
||||
The user's federation inbox. Refer to the [federation documentation](/federation).
|
||||
|
||||
Some instances may also have a shared inbox. Refer to [Instance Metadata](/entities/instance-metadata) for more information.
|
||||
</Property>
|
||||
<Property name="collections" type="UserCollections" required={true}>
|
||||
Collections related to the user. Must contain at least `outbox`, `followers`, `following`, and `featured`.
|
||||
|
||||
```typescript
|
||||
type URI = string;
|
||||
|
||||
type UserCollections = {
|
||||
outbox: URI;
|
||||
followers: URI;
|
||||
following: URI;
|
||||
featured: URI;
|
||||
// Same format as type on Extensions
|
||||
[key: ExtensionsKey]: URI;
|
||||
}
|
||||
```
|
||||
|
||||
All URIs must resolve to either a [Collection](/structures/collection) or a [URI Collection](/structures/collection#uri-collection) of the appropriate entities. Extensions may add additional collections.
|
||||
|
||||
### Outbox
|
||||
The user's federation outbox. Refer to the [federation documentation](/federation). [Collection](/structures/collection) of [Note](/entities/note) entities.
|
||||
|
||||
### Followers
|
||||
User's followers. [URI Collection](/structures/collection#uri-collection) of [User](/entities/user) entities.
|
||||
|
||||
### Following
|
||||
Users that the user follows. [URI Collection](/structures/collection#uri-collection) of [User](/entities/user) entities.
|
||||
|
||||
### Featured
|
||||
[Notes](/entities/note) that the user wants to feature (also known as "pin") on their profile. [Collection](/structures/collection) of [Note](/entities/note) entities. Only notes authored by the user can be featured.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
|
|
@ -174,14 +122,20 @@ Instance **must** be the host of the instance the user is on (hostname with opti
|
|||
"header": null,
|
||||
"indexable": false,
|
||||
"manually_approves_followers": false,
|
||||
"public_key": {
|
||||
"actor": "018ec082-0ae1-761c-b2c5-22275a611771",
|
||||
"algorithm": "ed25519",
|
||||
"key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
},
|
||||
"username": "aprl"
|
||||
}
|
||||
```
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
### Collections
|
||||
|
||||
The following [Collections](/structures/collection) are available:
|
||||
|
||||
- `outbox`: [Collection](/structures/collection) of notes authored by the user.
|
||||
- `followers`: [URI Collection](/structures/collection#uri-collection) of users that follow the user.
|
||||
- `following`: [URI Collection](/structures/collection#uri-collection) of users that the user follows.
|
||||
- `featured`: [Collection](/structures/collection) of notes that the user wants to feature on their profile ("pinned" notes).
|
||||
|
||||
These can be fetched using the [Federation API](/api/endpoints#entity-collections)
|
||||
|
|
@ -15,7 +15,11 @@ The Custom Emojis extension adds support for adding personalized emojis to feder
|
|||
<Property name="name" type="string" required={true}>
|
||||
Emoji name, surrounded by identification characters (for example, colons: `:happy_face:`).
|
||||
|
||||
Name must match the regex `^[a-zA-Z0-9_-]+$`.
|
||||
The following characters are allowed:
|
||||
- `a-z`
|
||||
- `A-Z`
|
||||
- `0-9`
|
||||
- `_`, `-`
|
||||
|
||||
Identification characters must not match the name regex (must not be alphanumeric/underscore/hyphen). There may only be two identification characters, one at the beginning and one at the end.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -26,13 +26,12 @@ This is useful as a way to centralize all of a user's many "alt accounts" into a
|
|||
|
||||
## Extension Definition
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Delegation">
|
||||
The Delegation extension uses an ID of `pub.versia:delegation`.
|
||||
|
||||
If the extension is present, exactly **one** of the fields **MUST** be specified:
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Delegation">
|
||||
<Property name="delegator" type="Reference">
|
||||
If this user performs actions on behalf on another user, **MUST** have a reference to that user.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -36,12 +36,6 @@ Refer to [Note](/entities/note#entity-definition)'s `group` property for how not
|
|||
It is similar to a [User](/entities/user)'s `manually_approves_followers` field.
|
||||
</Note>
|
||||
</Property>
|
||||
<Property name="members" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the group's members list. [URI Collection](/structures/collection#uri-collection) of [Users](/entities/user).
|
||||
</Property>
|
||||
<Property name="notes" type="URI" required={false} typeLink="/types#uri">
|
||||
URI of the group's associated notes. [URI Collection](/structures/collection#uri-collection) of [Notes](/entities/note).
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
|
||||
|
|
@ -68,6 +62,15 @@ Refer to [Note](/entities/note#entity-definition)'s `group` property for how not
|
|||
</Col>
|
||||
</Row>
|
||||
|
||||
### Collections
|
||||
|
||||
The following [Collections](/structures/collection) are available:
|
||||
|
||||
- `members`: [URI Collection](/structures/collection#uri-collection) of [Users](/entities/user) that are members of the group.
|
||||
- `notes`: [URI Collection](/structures/collection#uri-collection) of [Notes](/entities/note) that are associated with the group.
|
||||
|
||||
These can be fetched using the [Federation API](/api/endpoints#entity-collections)
|
||||
|
||||
## Subscribing to Groups
|
||||
|
||||
[Users](/entities/user) may "subscribe" to a Group in order to receive all [Notes](/entities/note) posted to it. The mechanism by which federation is handled is described at [the end of this document](#federation).
|
||||
|
|
@ -80,15 +83,16 @@ If the group accepts the subscription, the user will receive all notes posted to
|
|||
|
||||
Indicates that a [User](/entities/user) wishes to subscribe to a group.
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="GroupSubscribe">
|
||||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:groups/Subscribe`.
|
||||
</Property>
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="subscriber" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the [User](/entities/user) subscribing to the group.
|
||||
</Property>
|
||||
|
|
@ -117,15 +121,16 @@ Indicates that a [User](/entities/user) wishes to subscribe to a group.
|
|||
|
||||
Indicates that a [User](/entities/user) wishes to unsubscribe from a group.
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="GroupUnsubscribe">
|
||||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:groups/Unsubscribe`.
|
||||
</Property>
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="subscriber" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the [User](/entities/user) unsubscribing from the group.
|
||||
</Property>
|
||||
|
|
@ -152,7 +157,11 @@ Indicates that a [User](/entities/user) wishes to unsubscribe from a group.
|
|||
|
||||
### GroupSubscribeAccept
|
||||
|
||||
Indicates that a [Group](#entity-definition) has accepted a [User](/entities/user)'s subscription request. Should be signed by the instance hosting the group.
|
||||
Indicates that a [Group](#entity-definition) has accepted a [User](/entities/user)'s subscription request. Should be sent by the instance hosting the group.
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
|
@ -160,9 +169,6 @@ Indicates that a [Group](#entity-definition) has accepted a [User](/entities/use
|
|||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:groups/SubscribeAccept`.
|
||||
</Property>
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="subscriber" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the [User](/entities/user) subscribing to the group.
|
||||
</Property>
|
||||
|
|
@ -189,7 +195,11 @@ Indicates that a [Group](#entity-definition) has accepted a [User](/entities/use
|
|||
|
||||
### GroupSubscribeReject
|
||||
|
||||
Indicates that a [Group](#entity-definition) has rejected a [User](/entities/user)'s subscription request. Should be signed by the instance hosting the group.
|
||||
Indicates that a [Group](#entity-definition) has rejected a [User](/entities/user)'s subscription request. Should be sent by the instance hosting the group.
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
|
@ -197,9 +207,6 @@ Indicates that a [Group](#entity-definition) has rejected a [User](/entities/use
|
|||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:groups/SubscribeReject`.
|
||||
</Property>
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="subscriber" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the [User](/entities/user) subscribing to the group.
|
||||
</Property>
|
||||
|
|
@ -228,13 +235,17 @@ Indicates that a [Group](#entity-definition) has rejected a [User](/entities/use
|
|||
|
||||
Group federation represents a particularly challenging problem, as it requires a way to make sure every single [Note](/entities/note) posted to it is delivered to every single member of the group.
|
||||
|
||||
All [Notes](/entities/note) posted to a group (using the `group` field) must be sent to its instance's [shared inbox](/federation#inboxes). Groups do not have an inbox of their own.
|
||||
All [Notes](/entities/note) posted to a group (using the `group` field) must be sent to its instance's [inbox](/api/endpoints#inbox).
|
||||
|
||||
Once this is done, the group's instance must then federate this [Note](/entities/note) to every member of the group. However, this cannot be done the "normal way", as the group's instance does not have the private key to [sign](/signatures) the [Note](/entities/note).
|
||||
|
||||
### GroupFederate
|
||||
|
||||
The `GroupFederate` entity allows a group to federate a note to all of its members, without needing to sign the note itself. It contains a URI to the note being federated, which must be fetched by the receiving instances. This entity is signed by the group's instance.
|
||||
The `GroupFederate` entity allows a group to federate a note to all of its members, without needing to sign the note itself. It contains a URI to the note being federated, which must be fetched by the receiving instances. This entity is sent by the group's instance.
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
|
@ -242,9 +253,6 @@ The `GroupFederate` entity allows a group to federate a note to all of its membe
|
|||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:groups/Federate`.
|
||||
</Property>
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="note" type="URI" required={true} typeLink="/types#uri">
|
||||
URI of the note to federate.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -23,19 +23,12 @@ For example, let's consider the following scenario:
|
|||
|
||||
In this scenario, `jane.org` has no way of knowing what went wrong, as `joe.social` does not provide any feedback.
|
||||
|
||||
## Instance Metadata Extensions
|
||||
|
||||
This extension adds the following metadata to instances:
|
||||
## Sending Messages
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="InstanceMessagingExtension">
|
||||
<Property name="endpoint" type="string" required={true}>
|
||||
The endpoint to send federation debug messages to.
|
||||
To send a federation debug message, instances **should** make a `POST` request to the `/.versia/v0.6/messaging` endpoint with a simple text body containing a helpful message.
|
||||
|
||||
### Sending Messages
|
||||
|
||||
To send a federation debug message, instances **should** make a `POST` request to the appropriate endpoint with a simple text body containing a helpful message.
|
||||
No signatures are required (either on the request or response), and no response body is expected.
|
||||
|
||||
```text {{ "title": "Helpful Message" }}
|
||||
Validation failed for https://jane.org/users/nkGEd8eI98.
|
||||
|
|
@ -48,35 +41,3 @@ This extension adds the following metadata to instances:
|
|||
```text {{ "title": "Unhelpful Message" }}
|
||||
Federation failed.
|
||||
```
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
|
||||
<Col sticky>
|
||||
|
||||
```jsonc {{ "title": "Example Instance Metadata" }}
|
||||
{
|
||||
"type": "InstanceMetadata",
|
||||
"name": "Jane.org",
|
||||
"software": {
|
||||
"name": "Versia Server",
|
||||
"version": "0.7.0"
|
||||
},
|
||||
"compatibility": {
|
||||
"versions": [
|
||||
"0.5.0"
|
||||
],
|
||||
"extensions": [
|
||||
"pub.versia:reactions",
|
||||
"pub.versia:polls",
|
||||
"pub.versia:reports",
|
||||
"pub.versia:instance_messaging"
|
||||
]
|
||||
},
|
||||
"host": "jane.org",
|
||||
"created_at": "2021-07-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
@ -99,7 +99,7 @@ Extensions **may** choose to register their own interaction types (such as `pub.
|
|||
|
||||
### Handling Permission Errors
|
||||
|
||||
Implementations that find a user attempting to create an interaction they are not allowed to **MUST** return a `403 Forbidden` HTTP status code when processing the Note during federation. The Note **must** also be discarded.
|
||||
Implementations that find a user attempting to create an interaction they are not allowed to **MAY** return a `403 Forbidden` HTTP status code when processing the Note during federation, or use the [Instance Messaging](/extensions/instance-messaging) extension. The Note **must** be discarded.
|
||||
|
||||
It is important for implementations to backfill any related [Collections](/structures/collection)/[URI Collections](/structures/collection#uri-collection) (e.g. user followers) in order to not incorrectly reject Notes based off of outdated data.
|
||||
|
||||
|
|
|
|||
|
|
@ -90,39 +90,15 @@ To undo a like or dislike, a [Delete](/entities/delete) entity should be used. T
|
|||
|
||||
The Likes extension adds the following collections to the [User](/entities/user) entity:
|
||||
|
||||
- `likes`: A [URI Collection](/structures/collection#uri-collection) of all the notes the user has liked.
|
||||
- `dislikes`: A [URI Collection](/structures/collection#uri-collection) of all the notes the user has disliked.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"type": "User",
|
||||
...
|
||||
"collections": {
|
||||
...
|
||||
"pub.versia:likes/Likes": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe/likes",
|
||||
"pub.versia:likes/Dislikes": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe/dislikes"
|
||||
}
|
||||
}
|
||||
```
|
||||
- `pub.versia:likes/Likes`: [URI Collection](/structures/collection#uri-collection) of all the notes the user has liked.
|
||||
- `pub.versia:likes/Dislikes`: [URI Collection](/structures/collection#uri-collection) of all the notes the user has disliked.
|
||||
|
||||
## Note Collections
|
||||
|
||||
The Likes extension adds the following collections to the [Note](/entities/note) entity:
|
||||
|
||||
- `likes`: A [URI Collection](/structures/collection#uri-collection) of all the likes the note has received.
|
||||
- `dislikes`: A [URI Collection](/structures/collection#uri-collection) of all the dislikes the note has received.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"type": "Note",
|
||||
...
|
||||
"collections": {
|
||||
...
|
||||
"pub.versia:likes/Likes": "https://example.com/notes/fmKZ763jzIU8/likes",
|
||||
"pub.versia:likes/Dislikes": "https://example.com/notes/fmKZ763jzIU8/dislikes"
|
||||
}
|
||||
}
|
||||
```
|
||||
- `pub.versia:likes/Likes`: [URI Collection](/structures/collection#uri-collection) of all the likes the note has received.
|
||||
- `pub.versia:likes/Dislikes`: [URI Collection](/structures/collection#uri-collection) of all the dislikes the note has received.
|
||||
|
||||
## Interaction Types
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,13 @@ Migration happens in three steps:
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Migration">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:migration/Migration`.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const metadata = {
|
|||
|
||||
Versia provides a set of core entities and structures to build a barebones social network. However, it is not possible nor desirable to cover every use case. This is where extensions come in, allowing parts of the system to be extended or replaced with custom functionality. {{ className: 'lead' }}
|
||||
|
||||
By design, extensions can be mitchmatched in any combination, without requiring any changes to the core system. This allows for a high degree of customization and flexibility. Implementations that do not support a particular extension can simply ignore it without any issues.
|
||||
By design, extensions can be implemented in any combination, without requiring any changes to the core system. This allows for a high degree of customization and flexibility. Implementations that do not support a particular extension can simply ignore it without any issues.
|
||||
|
||||
Extensions **should** be standardized and publicly documented.
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ Extensions can be found in two places: an [Entity](/entities#entity-definition)'
|
|||
<Property name="other">
|
||||
Other properties of the custom entity. These are specific to the extension, and should be documented by the extension author.
|
||||
|
||||
Note that `id`, `uri` and `created_at` are still required for custom entities, unless the extension author specifies otherwise.
|
||||
Note that `id` and `created_at` are still required for custom entities, unless the extension author specifies otherwise.
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -53,18 +53,7 @@ User reactions are (like every other entity) federated to all followers, and can
|
|||
|
||||
The Likes extension adds the following collections to the [Note](/entities/note) entity:
|
||||
|
||||
- `reactions`: A [URI Collection](/structures/collection#uri-collection) of all the reactions to the note.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"type": "Note",
|
||||
...
|
||||
"collections": {
|
||||
...
|
||||
"pub.versia:reactions/Reactions": "https://example.com/publications/f08a124e-fe90-439e-8be4-15a428a72a19/reactions"
|
||||
}
|
||||
}
|
||||
```
|
||||
- `pub.versia:reactions/Reactions`: [URI Collection](/structures/collection#uri-collection) of all the reactions to the note.
|
||||
|
||||
## Interaction Types
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ When an instance receives a report, it *should* be reviewed by a moderator or ad
|
|||
|
||||
## Entity Definition
|
||||
|
||||
<Warning>
|
||||
This entity is a [**Transient Entity**](/entities#transient-entities).
|
||||
</Warning>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="Report">
|
||||
<Property name="uri" type="null" required={false}>
|
||||
This is a [**Transient Entity**](/entities#transient-entities) and does not have a URI.
|
||||
</Property>
|
||||
<Property name="type" type="string" required={true}>
|
||||
Must be `pub.versia:reports/Report`.
|
||||
</Property>
|
||||
|
|
|
|||
|
|
@ -49,18 +49,7 @@ When a user shares a note, the note's original author **must** receive the entit
|
|||
|
||||
The Share extension adds the following collections to the [Note](/entities/note) entity:
|
||||
|
||||
- `shares`: A [URI Collection](/structures/collection#uri-collection) of all the shares of the note.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"type": "Note",
|
||||
...
|
||||
"collections": {
|
||||
...
|
||||
"pub.versia:share/Shares": "https://example.com/notes/fmKZ763jzIU8/shares"
|
||||
}
|
||||
}
|
||||
```
|
||||
- `pub.versia:share/Shares`: [URI Collection](/structures/collection#uri-collection) of all the shares of the note.
|
||||
|
||||
## Interaction Types
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
export const metadata = {
|
||||
title: 'WebSocket Extension',
|
||||
description:
|
||||
'The WebSocket extension adds support for real-time communication between instances.',
|
||||
}
|
||||
|
||||
# WebSockets Extension
|
||||
|
||||
<Note>
|
||||
This document is **provided for informative purposes** and should not be used in a production environment. It is subject to change.
|
||||
|
||||
If internal testing proves unsuccessful at solving the problems with the HTTP delivery method, this document may be abandoned.
|
||||
</Note>
|
||||
|
||||
Typically, communication between Versia instances is done via HTTP. However, HTTP suffers from some limitations, such as high latency and heavy overhead for small messages, making it less suitable for exchanging large amounts of entities at acceptable speeds. {{ className: 'lead' }}
|
||||
|
||||
This extension aims to address these limitations by adding support for the exchange of entities using WebSockets.
|
||||
|
||||
## Message Format
|
||||
|
||||
Messages sent over the WebSocket connection are JSON objects.
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<Properties name="WebSocketMessage">
|
||||
<Property name="signature" type="string" required={true}>
|
||||
Same as the `Versia-Signature` header in HTTP requests.
|
||||
</Property>
|
||||
<Property name="signed_at" type="string" required={true}>
|
||||
Same as the `Versia-Signed-At` header in HTTP requests.
|
||||
</Property>
|
||||
<Property name="signed_by" type="URI" required={true}>
|
||||
Same as the `Versia-Signed-By` header in HTTP requests.
|
||||
</Property>
|
||||
<Property name="entity" type="Entity" required={true} typeLink="/entities">
|
||||
Same as the request body in HTTP requests. Must be a string (stringified JSON), not JSON.
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
|
||||
<Col sticky>
|
||||
|
||||
```jsonc {{ 'title': 'Example Message' }}
|
||||
{
|
||||
"signature": "/CjB2L9bcvRg+uP19B4/rqy7Ji9/cqMFPlL3GVCIndnQjYyOpBzJEAl9weDnXm7Jrqa3y6sBC+EYWKThO2r9Bw==",
|
||||
"signed_at": "1729241807",
|
||||
"signed_by": "https://bongo.social/users/63a00ab3-39b1-49eb-b88e-ed65d2361f3e",
|
||||
"entity": "{\"id\":\"9a8928b6-2526-4979-aab1-ef2f88cd5700\",\"type\":\"Delete\",\"created_at\":\"2022-01-01T12:00:00Z\",\"author\":\"https://bongo.social/users/63a00ab3-39b1-49eb-b88e-ed65d2361f3e\",\"deleted\":\"https://bongo.social/notes/54059ce2-9332-46fa-bf6a-598b5493b81b\"}"
|
||||
}
|
||||
```
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
## Connection Establishment
|
||||
|
||||
...
|
||||
|
|
@ -49,17 +49,17 @@ Accept: application/jrd+json
|
|||
|
||||
## Instance Discovery
|
||||
|
||||
Instaance metadata can be accessed by making a `GET` request to the instance's Versia metadata endpoint, which is located at `/.well-known/versia`. This endpoint does not need any signatures in the output.
|
||||
Instaance metadata can be accessed by making a `GET` request to the instance's Versia metadata endpoint, documented in the [Endpoints](/api/endpoints#instance-metadata) document.
|
||||
|
||||
### Example
|
||||
|
||||
To discover the metadata of the instance `versia.social`, an instance would make a `GET` request to `https://versia.social/.well-known/versia`.
|
||||
To discover the metadata of the instance `versia.social`, an instance would make a `GET` request to `https://versia.social/.versia/v0.6/instance`.
|
||||
|
||||
This endpoint will return an [InstanceMetadata](/entities/instance-metadata) entity.
|
||||
|
||||
```http {{ 'title': 'Example Request' }}
|
||||
GET /.well-known/versia HTTP/1.1
|
||||
Accept: application/json
|
||||
GET /.versia/v0.6/instance HTTP/1.1
|
||||
Accept: application/vnd.versia+json
|
||||
```
|
||||
|
||||
```jsonc {{ 'title': 'Example Response' }}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ curl https://b.social/.well-known/webfinger?resource=acct:joe@b.social -H "Accep
|
|||
"links": [
|
||||
{ // [!code focus:5]
|
||||
"rel": "self",
|
||||
"type": "application/json",
|
||||
"href": "https://b.social/users/joe"
|
||||
"type": "application/vnd.versia+json",
|
||||
"href": "https://b.social/.versia/entities/User/joe"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -58,12 +58,12 @@ curl https://b.social/.well-known/webfinger?resource=acct:joe@b.social -H "Accep
|
|||
`a.social` fetches the user profile of `joe` from `b.social` using the URL provided in the WebFinger response.
|
||||
|
||||
```bash
|
||||
curl https://b.social/users/joe \
|
||||
-H "Accept: application/json" \
|
||||
curl https://b.social/.versia/entities/User/joe \
|
||||
-H "Accept: application/vnd.versia+json" \
|
||||
-H "User-Agent: CoolServer/1.0 (https://coolserver.com)" \
|
||||
# The request is signed by a.social's instance private key
|
||||
-H "Versia-Signature: /CjB2L9bcvRg+uP19B4/rqy7Ji9/cqMFPlL3GVCIndnQjYyOpBzJEAl9weDnXm7Jrqa3y6sBC+EYWKThO2r9Bw==" \
|
||||
-H "Versia-Signed-By: https://a.social/users/alice" \
|
||||
-H "Versia-Signed-By: a.social" \
|
||||
-H "Versia-Signed-At: 1729241687"
|
||||
```
|
||||
|
||||
|
|
@ -79,13 +79,8 @@ curl https://b.social/users/joe \
|
|||
"content": "https://cdn.b.social/avatars/joe.webp",
|
||||
"remote": true
|
||||
}
|
||||
}, // [!code focus:8]
|
||||
}, // [!code focus:3]
|
||||
"display_name": "Joe Swanson (Winter Arc :gigachad:)",
|
||||
"public_key": {
|
||||
"actor": "b.social:bde22zi3ca8762",
|
||||
"algorithm": "ed25519",
|
||||
"key": "MCowBQYDK2VwAyEAOSCcfsde0Ya3vf/P6lzgK0pA8qCISqneaze3omLlQCQ="
|
||||
},
|
||||
"username": "joe",
|
||||
"extensions": {
|
||||
"pub.versia:custom_emojis": {
|
||||
|
|
@ -142,13 +137,13 @@ It is now time for `a.social` to send the note to `joe`.
|
|||
`a.social` sends the note to `joe`'s inbox at `b.social`.
|
||||
|
||||
```bash
|
||||
curl -X POST https://b.social/inbox \
|
||||
-H "Content-Type: application/json; charset=utf-8" \
|
||||
curl -X POST https://b.social/.versia/v0.6/inbox \
|
||||
-H "Content-Type: application/vnd.versia+json; charset=utf-8" \
|
||||
-H "Accept: application/json" \
|
||||
-H "User-Agent: CoolerServer/1.0 (https://coolerserver.com)" \
|
||||
# The request is signed by Alice's private key
|
||||
-H "Versia-Signature: 9BrfplAPVH6OEqlV5eX7MazaZAInSCPODZcBEvMliBi/OwfbCAsezlb0O9jUX9ZcbBA68ThA4WUgS9V+42rfAQ==" \
|
||||
-H "Versia-Signed-By: https://a.social/users/alice" \
|
||||
-H "Versia-Signed-By: a.social" \
|
||||
-H "Versia-Signed-At: 1733051946"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ ALL kinds of HTTP requests/responses between instances **MUST** include a [Signa
|
|||
<Col>
|
||||
<Properties name="HTTP Request">
|
||||
<Property name="Accept" type="string" required={true}>
|
||||
Must include `application/json`.
|
||||
Must include `application/vnd.versia+json`, unless specified otherwise.
|
||||
</Property>
|
||||
<Property name="Content-Type" type="string" required={true}>
|
||||
Must include `application/json; charset=utf-8`, if the request has a body.
|
||||
Must include `application/vnd.versia+json; charset=utf-8`, if the request has a body.
|
||||
</Property>
|
||||
<Property name="Versia-Signature" type="string" required={false}>
|
||||
See [Signatures](/signatures) for more information.
|
||||
|
|
@ -37,11 +37,12 @@ ALL kinds of HTTP requests/responses between instances **MUST** include a [Signa
|
|||
</Col>
|
||||
<Col sticky>
|
||||
```http {{ 'title': 'Example Request' }}
|
||||
POST https://bob.com/users/1/inbox HTTP/1.1
|
||||
POST https://bob.com/.versia/v0.6/inbox HTTP/1.1
|
||||
# This specific endpoint returns plain JSON
|
||||
Accept: application/json
|
||||
User-Agent: CoolServer/1.0 (https://coolserver.com)
|
||||
Versia-Signature: /CjB2L9bcvRg+uP19B4/rqy7Ji9/cqMFPlL3GVCIndnQjYyOpBzJEAl9weDnXm7Jrqa3y6sBC+EYWKThO2r9Bw==
|
||||
Versia-Signed-By: https://example.com/users/1
|
||||
Versia-Signed-By: example.com
|
||||
Versia-Signed-At: 1729241687
|
||||
```
|
||||
</Col>
|
||||
|
|
@ -63,7 +64,7 @@ IETF draft [draft-polli-ratelimit-headers-02](https://www.ietf.org/archive/id/dr
|
|||
<Col>
|
||||
<Properties name="HTTP Response">
|
||||
<Property name="Content-Type" type="string" required={true}>
|
||||
Must include `application/json; charset=utf-8`.
|
||||
Must include `application/vnd.versia+json; charset=utf-8`, unless specified otherwise.
|
||||
</Property>
|
||||
<Property name="Versia-Signature" type="string" required={false}>
|
||||
See [Signatures](/signatures) for more information.
|
||||
|
|
@ -79,9 +80,9 @@ IETF draft [draft-polli-ratelimit-headers-02](https://www.ietf.org/archive/id/dr
|
|||
<Col sticky>
|
||||
```http {{ 'title': 'Example Response' }}
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
Content-Type: application/vnd.versia+json; charset=utf-8
|
||||
Versia-Signature: /CjB2L9bcvRg+uP19B4/rqy7Ji9/cqMFPlL3GVCIndnQjYyOpBzJEAl9weDnXm7Jrqa3y6sBC+EYWKThO2r9Bw==+7BvnWKITyGyTwHbb6fVYwRx1I
|
||||
Versia-Signed-By: https://example.com/users/1
|
||||
Versia-Signed-By: example.com
|
||||
Versia-Signed-At: 1729241717
|
||||
```
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -21,24 +21,22 @@ Federation is built on the [HyperText Transfer Protocol (HTTP)](https://tools.ie
|
|||
|
||||
## Inboxes
|
||||
|
||||
Ever [User](/entities/user) has a personal HTTP endpoint called an inbox (e.g., `/users/3/inbox`). This endpoint is used to send and receive messages between users and instances. Messages are sent as JSON payloads over HTTP `POST` requests.
|
||||
<Note>
|
||||
See the [Inbox Endpoint](/api/endpoints#inbox) for more information on the inbox endpoint.
|
||||
</Note>
|
||||
|
||||
Ever [Instance](/entities/instance-metadata) has a personal HTTP endpoint called an inbox (`/.versia/v0.6/inbox`). This endpoint is used to send and receive messages between users and instances. Messages are sent as JSON payloads over HTTP `POST` requests.
|
||||
|
||||
Let's consider the following example:
|
||||
|
||||
> Alice, on the instance `alice.example`, sends a message to Bob on the instance `bob.example`.
|
||||
|
||||
To perform this action, Alice's instance sends a `POST` request to Bob's inbox endpoint, which can be found by checking the `inbox` field in Bob's [User](/entities/user) entity. This request contains the [Note](/entities/note) entity that Alice wants to send to Bob, with [the appropriate metadata](/federation/http) to ensure the message is valid.
|
||||
To perform this action, Alice's instance sends a `POST` request to Bob's instance's inbox endpoint. This request contains the [Note](/entities/note) entity that Alice wants to send to Bob, with [the appropriate metadata](/federation/http) to ensure the message is valid.
|
||||
|
||||
Bob's instance receives the message and processes it according to the rules defined in the [Validation](/federation/validation) document.
|
||||
|
||||
### Shared Inboxes
|
||||
|
||||
In addition to personal inboxes, instances can also have shared inboxes. Shared inboxes are a single per-instance endpoint that can receive messages for multiple users. This is useful for instances that want to reduce the number of HTTP requests they need to handle.
|
||||
|
||||
Shared inboxes are defined in the [Instance Metadata](/entities/instance-metadata) entity. If supported, they should be used in place of personal inboxes.
|
||||
|
||||
## Outboxes
|
||||
|
||||
In addition to inboxes, every user has an outbox (e.g., `/users/3/outbox`). The outbox is simply a [Collection](/structures/collection) of all the messages that a user has sent. When a user sends a message to another user, a copy of that message is accessible in the sender's outbox.
|
||||
In addition to inboxes, every user has an outbox (at `/.versia/v0.6/entities/User/<id>/collections/outbox`). The outbox is simply a [Collection](/structures/collection) of all the messages that a user has sent. When a user sends a message to another user, a copy of that message is accessible in the sender's outbox.
|
||||
|
||||
Outboxes are very useful for "backfilling" data when a new instance joins the network. By resolving the outboxes of all new users it encounters, a new instance can quickly catch up on all old messages.
|
||||
|
|
@ -18,7 +18,7 @@ Versia uses cryptographic signatures to ensure the integrity and authenticity of
|
|||
|
||||
A signature consists of a series of headers in an HTTP request. The following headers are used:
|
||||
- **`Versia-Signature`**: The signature itself, encoded in base64.
|
||||
- **`Versia-Signed-By`**: URI of the user who signed the request, [or the string `instance $1`, to represent the instance, where `$1` is the instance's host](/entities/instance-metadata#the-null-author).
|
||||
- **`Versia-Signed-By`**: Hostname of the instance that signed the request. This **MUST** be a hostname reachable over HTTPS (including port number if applicable), and **MUST NOT** be a URL.
|
||||
- **`Versia-Signed-At`**: The current Unix timestamp, in seconds (no milliseconds), when the request was signed. Timezone must be UTC, like all Unix timestamps.
|
||||
|
||||
Signatures are **required on ALL federation traffic**. If a request does not have a signature, it **MUST** be rejected. Specifically, signatures must be put on:
|
||||
|
|
@ -61,14 +61,14 @@ To verify a signature, the instance must:
|
|||
|
||||
The following example is written in TypeScript using the WebCrypto API.
|
||||
|
||||
`@bob`, from `bob.com`, wants to sign a request to `alice.com`. The request is a `POST` to `/notes`, with the following body:
|
||||
`@bob`, from `bob.com`, wants to sign a request to `alice.com`. The request is a `POST` to `/.versia/v0.6/inbox`, with the following body:
|
||||
```json
|
||||
{
|
||||
"content": "Hello, world!"
|
||||
}
|
||||
```
|
||||
|
||||
Bob can be found at `https://bob.com/users/bf44e6ad-7c0a-4560-9938-cf3fd4066511`. His ed25519 private key, encoded in Base64 PKCS8, is `MC4CAQAwBQYDK2VwBCIEILrNXhbWxC/MhKQDsJOAAF1FH/R+Am5G/eZKnqNum5ro`.
|
||||
Bob can be found at `https://bob.com/.versia/v0.6/entities/User/bf44e6ad-7c0a-4560-9938-cf3fd4066511`. His instance's ed25519 private key, encoded in Base64 PKCS8, is `MC4CAQAwBQYDK2VwBCIEILrNXhbWxC/MhKQDsJOAAF1FH/R+Am5G/eZKnqNum5ro`.
|
||||
|
||||
Here's how Bob would sign the request:
|
||||
```typescript
|
||||
|
|
@ -97,7 +97,7 @@ const digest = await crypto.subtle.digest(
|
|||
);
|
||||
|
||||
const stringToSign =
|
||||
`post /notes ${timestamp} ${Buffer.from(digest).toString("base64")}`;
|
||||
`post /.versia/v0.6/inbox ${timestamp} ${Buffer.from(digest).toString("base64")}`;
|
||||
|
||||
const signature = await crypto.subtle.sign(
|
||||
"Ed25519",
|
||||
|
|
@ -113,19 +113,19 @@ To send the request, Bob would use the following code:
|
|||
```typescript
|
||||
const headers = new Headers();
|
||||
|
||||
headers.set("Versia-Signed-By", "https://bob.com/users/bf44e6ad-7c0a-4560-9938-cf3fd4066511");
|
||||
headers.set("Versia-Signed-By", "bob.com");
|
||||
headers.set("Versia-Signed-At", timestamp);
|
||||
headers.set("Versia-Signature", base64Signature);
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
const response = await fetch("https://alice.com/notes", {
|
||||
const response = await fetch("https://alice.com/.versia/v0.6/inbox", {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: content,
|
||||
});
|
||||
```
|
||||
|
||||
On Alice's side, she would verify the signature using Bob's public key. Here, we assume that Alice has Bob's public key stored in a variable called `publicKey` (during real federation, this would be fetched from Bob's profile).
|
||||
On Alice's side, she would verify the signature using Bob's instance's public key. Here, we assume that Alice has Bob's instance's public key stored in a variable called `publicKey` (during real federation, this would be fetched from the instance's [metadata endpoint](/api/endpoints#instance-metadata)).
|
||||
|
||||
```typescript
|
||||
const method = request.method.toLowerCase();
|
||||
|
|
@ -165,7 +165,7 @@ Public keys are always encoded using `base64` and must be in SPKI format. You wi
|
|||
<Note>
|
||||
This is **not** the same as the key's raw bytes.
|
||||
|
||||
This is also not related to the commonly used "PEM" format.
|
||||
This is also not the commonly used "PEM" format.
|
||||
</Note>
|
||||
|
||||
```typescript {{ title: "Example using TypeScript and the WebCrypto API" }}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ export const metadata = {
|
|||
|
||||
Collections are a way to represent paginated groups of entities. They are used everywhere lists of entities can be found, such as a user's outbox. {{ className: 'lead' }}
|
||||
|
||||
Pages should be limited to a reasonable number of entities, such as 20 or 80.
|
||||
|
||||
<Note>
|
||||
As Collections are independent and not part of a larger entity (like [ContentFormat](/structures/content-format)), they should have a valid [Signature](/signatures).
|
||||
</Note>
|
||||
|
|
@ -21,28 +19,9 @@ Pages should be limited to a reasonable number of entities, such as 20 or 80.
|
|||
<Property name="author" type="URI | null" required={true} typeLink="/types#uri">
|
||||
Author of the collection. Usually the user who owns the collection. [Can be set to `null` to represent the instance](/entities/instance-metadata#the-null-author).
|
||||
</Property>
|
||||
<Property name="first" type="URI" required={true} typeLink="/types#uri">
|
||||
URI to the first page of the collection. Query parameters are allowed.
|
||||
</Property>
|
||||
<Property name="last" type="URI" required={true} typeLink="/types#uri">
|
||||
URI to the last page of the collection. Query parameters are allowed.
|
||||
|
||||
If the collection only has one page, this should be the same as `first`.
|
||||
</Property>
|
||||
<Property name="total" type="number" required={true} numberType="u64">
|
||||
Total number of entities in the collection, across all pages.
|
||||
</Property>
|
||||
<Property name="next" type="URI" required={false} typeLink="/types#uri">
|
||||
URI to the next page of the collection. Query parameters are allowed.
|
||||
|
||||
If there is no next page, this should be `null`.
|
||||
</Property>
|
||||
<Property name="previous" type="URI" required={false} typeLink="/types#uri">
|
||||
URI to the previous page of the collection. Query parameters are allowed.
|
||||
|
||||
|
||||
If there is no previous page, this should be `null`.
|
||||
</Property>
|
||||
<Property name="items" type="Entity[]" required={true}>
|
||||
Collection contents. Must be an array of entities.
|
||||
</Property>
|
||||
|
|
@ -54,11 +33,7 @@ Pages should be limited to a reasonable number of entities, such as 20 or 80.
|
|||
```jsonc {{ 'title': 'Example Collection' }}
|
||||
{
|
||||
"author": "https://versia.social/users/018ec082-0ae1-761c-b2c5-22275a611771",
|
||||
"first": "https://versia.social/users/018ec082-0ae1-761c-b2c5-22275a611771/outbox?page=1",
|
||||
"last": "https://versia.social/users/018ec082-0ae1-761c-b2c5-22275a611771/outbox?page=3",
|
||||
"total": 46,
|
||||
"next": "https://versia.social/users/018ec082-0ae1-761c-b2c5-22275a611771/outbox?page=2",
|
||||
"previous": null,
|
||||
"items": [
|
||||
{
|
||||
"id": "456df8ed-daf1-4062-abab-491071c7b8dd",
|
||||
|
|
@ -92,27 +67,9 @@ URI Collections are identical to regular collections, but they contain only URIs
|
|||
<Property name="author" type="URI | null" required={true} typeLink="/types#uri">
|
||||
Author of the collection. Usually the user who owns the collection. [Can be set to `null` to represent the instance](/entities/instance-metadata#the-null-author).
|
||||
</Property>
|
||||
<Property name="first" type="URI" required={true} typeLink="/types#uri">
|
||||
URI to the first page of the collection. Query parameters are allowed.
|
||||
</Property>
|
||||
<Property name="last" type="URI" required={true} typeLink="/types#uri">
|
||||
URI to the last page of the collection. Query parameters are allowed.
|
||||
|
||||
If the collection only has one page, this should be the same as `first`.
|
||||
</Property>
|
||||
<Property name="total" type="number" required={true} numberType="u64">
|
||||
Total number of entities in the collection, across all pages.
|
||||
</Property>
|
||||
<Property name="next" type="URI" required={false} typeLink="/types#uri">
|
||||
URI to the next page of the collection. Query parameters are allowed.
|
||||
|
||||
If there is no next page, this should be `null`.
|
||||
</Property>
|
||||
<Property name="previous" type="URI" required={false} typeLink="/types#uri">
|
||||
URI to the previous page of the collection. Query parameters are allowed.
|
||||
|
||||
If there is no previous page, this should be `null`.
|
||||
</Property>
|
||||
<Property name="items" type="URI[]" required={true}>
|
||||
Collection contents. Must be an array of URIs.
|
||||
</Property>
|
||||
|
|
|
|||
Loading…
Reference in a new issue