2025-01-01 00:44:04 +01:00
|
|
|
export const metadata = {
|
|
|
|
|
title: 'Federation Example',
|
|
|
|
|
description:
|
|
|
|
|
'A description of how a typical federation flow might look like',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Example
|
|
|
|
|
|
|
|
|
|
This page describes a typical federation flow between two servers, `a.social` and `b.social`, in several different contexts. {{ className: 'lead' }}
|
|
|
|
|
|
|
|
|
|
<Note>
|
|
|
|
|
All examples, domains, names and timestamps are **fictional** and are used **for illustrative purposes only**.
|
|
|
|
|
|
|
|
|
|
Some details have been slightly simplified for clarity.
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
## Sending a Note
|
|
|
|
|
|
|
|
|
|
`@alice` on `a.social` creates a note with the following content:
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
Hello, @joe@b.social! How are you doing today?
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`@alice` has mentioned `@joe@b.social` in the note.
|
|
|
|
|
|
|
|
|
|
### Resolving the Mention
|
|
|
|
|
|
|
|
|
|
`a.social` resolves the mention by querying `b.social` for the user `joe` using WebFinger.
|
|
|
|
|
|
|
|
|
|
```bash {{ title: "cURL example" }}
|
|
|
|
|
curl https://b.social/.well-known/webfinger?resource=acct:joe@b.social -H "Accept: application/json"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`b.social` responds with the following JSON:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"subject": "acct:joe@b.social",
|
|
|
|
|
"links": [
|
|
|
|
|
{ // [!code focus:5]
|
|
|
|
|
"rel": "self",
|
2025-05-05 14:08:20 +02:00
|
|
|
"type": "application/vnd.versia+json",
|
|
|
|
|
"href": "https://b.social/.versia/entities/User/joe"
|
2025-01-01 00:44:04 +01:00
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<Note>
|
|
|
|
|
In a real Versia implementation, usernames would **not** be included in user profile's URL, as they can be changed. Instead, the `id` could be used.
|
|
|
|
|
|
|
|
|
|
This is done for simplicity in this example.
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
### Fetching the User
|
|
|
|
|
|
|
|
|
|
`a.social` fetches the user profile of `joe` from `b.social` using the URL provided in the WebFinger response.
|
|
|
|
|
|
|
|
|
|
```bash
|
2025-05-05 14:08:20 +02:00
|
|
|
curl https://b.social/.versia/entities/User/joe \
|
|
|
|
|
-H "Accept: application/vnd.versia+json" \
|
2025-01-01 00:44:04 +01:00
|
|
|
-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==" \
|
2025-05-05 14:08:20 +02:00
|
|
|
-H "Versia-Signed-By: a.social" \
|
2025-01-01 00:44:04 +01:00
|
|
|
-H "Versia-Signed-At: 1729241687"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`b.social` responds with the following JSON:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"id": "bde22zi3ca8762", // [!code focus:10]
|
|
|
|
|
"type": "User",
|
|
|
|
|
"created_at": "2024-10-13T18:48:19Z",
|
|
|
|
|
"avatar": {
|
|
|
|
|
"image/webp": {
|
|
|
|
|
"content": "https://cdn.b.social/avatars/joe.webp",
|
|
|
|
|
"remote": true
|
|
|
|
|
}
|
2025-05-05 14:08:20 +02:00
|
|
|
}, // [!code focus:3]
|
2025-01-01 00:44:04 +01:00
|
|
|
"display_name": "Joe Swanson (Winter Arc :gigachad:)",
|
|
|
|
|
"username": "joe",
|
|
|
|
|
"extensions": {
|
|
|
|
|
"pub.versia:custom_emojis": {
|
|
|
|
|
"emojis": [
|
|
|
|
|
{
|
|
|
|
|
"name": ":gigachad:",
|
|
|
|
|
"content": {
|
|
|
|
|
"image/png": {
|
|
|
|
|
"content": "https://cdn.b.social/emojis/gigachad.png",
|
|
|
|
|
"remote": true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`a.social` now has the user profile of `joe` and can display the note with the correct user information.
|
|
|
|
|
|
|
|
|
|
### Serializing the Note
|
|
|
|
|
|
|
|
|
|
Finally, `a.social` serializes the note to send it to `joe`.
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"id": "782addd9-c051-4eea-8ba4-23d561d0c5bb", // [!code focus:6]
|
|
|
|
|
"type": "Note",
|
|
|
|
|
"created_at": "2024-12-01T12:19:06Z",
|
2025-04-21 18:17:45 +02:00
|
|
|
"author": "alice",
|
|
|
|
|
"category": "microblog", // [!code focus:11]
|
2025-01-01 00:44:04 +01:00
|
|
|
"content": {
|
|
|
|
|
"text/html": {
|
2025-04-21 18:17:45 +02:00
|
|
|
"content": "Hello, <a class=\"u-url mention\" href=\"https://b.social/users/bde22zi3ca8762\">@joe@b.social</a>! How are you doing today?",
|
2025-01-01 00:44:04 +01:00
|
|
|
"remote": false,
|
|
|
|
|
},
|
|
|
|
|
"text/plain": {
|
|
|
|
|
"content": "Hello, @joe@b.social! How are you doing today?",
|
|
|
|
|
"remote": false,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"group": "public",
|
|
|
|
|
"mentions": [ // [!code focus:3]
|
2025-04-21 18:17:45 +02:00
|
|
|
"b.social:bde22zi3ca8762"
|
2025-01-01 00:44:04 +01:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It is now time for `a.social` to send the note to `joe`.
|
|
|
|
|
|
|
|
|
|
### Sending the Note
|
|
|
|
|
|
|
|
|
|
`a.social` sends the note to `joe`'s inbox at `b.social`.
|
|
|
|
|
|
|
|
|
|
```bash
|
2025-05-05 14:08:20 +02:00
|
|
|
curl -X POST https://b.social/.versia/v0.6/inbox \
|
|
|
|
|
-H "Content-Type: application/vnd.versia+json; charset=utf-8" \
|
2025-01-01 00:44:04 +01:00
|
|
|
-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==" \
|
2025-05-05 14:08:20 +02:00
|
|
|
-H "Versia-Signed-By: a.social" \
|
2025-01-01 00:44:04 +01:00
|
|
|
-H "Versia-Signed-At: 1733051946"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`b.social` responds with a `202 Accepted` status code.
|
|
|
|
|
|
|
|
|
|
### Displaying the Note
|
|
|
|
|
|
|
|
|
|
The software on `b.social` processes the note and shows it to `joe` using whatever interface it has.
|
|
|
|
|
|
|
|
|
|
`joe` can now see the note from `@alice` on `a.social` and respond to it.
|