diff --git a/app/federation/example/page.mdx b/app/federation/example/page.mdx new file mode 100644 index 0000000..0709a03 --- /dev/null +++ b/app/federation/example/page.mdx @@ -0,0 +1,174 @@ +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' }} + + + All examples, domains, names and timestamps are **fictional** and are used **for illustrative purposes only**. + + Some details have been slightly simplified for clarity. + + +## 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", + "type": "application/json", + "href": "https://b.social/users/joe" + } + ] +} +``` + + + 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. + + +### Fetching the User + +`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" \ + -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-At: 1729241687" +``` + +`b.social` responds with the following JSON: + +```json +{ + "id": "bde22zi3ca8762", // [!code focus:10] + "type": "User", + "uri": "https://b.social/users/joe", + "created_at": "2024-10-13T18:48:19Z", + "avatar": { + "image/webp": { + "content": "https://cdn.b.social/avatars/joe.webp", + "remote": true + } + }, + "collections": { + "featured": "https://b.social/users/joe/featured", + "followers": "https://b.social/users/joe/followers", + "following": "https://b.social/users/joe/following", + "outbox": "https://b.social/users/joe/outbox" + }, // [!code focus:9] + "display_name": "Joe Swanson (Winter Arc :gigachad:)", + "inbox": "https://b.social/inbox", + "public_key": { + "actor": "https://b.social/users/joe", + "algorithm": "ed25519", + "key": "MCowBQYDK2VwAyEAOSCcfsde0Ya3vf/P6lzgK0pA8qCISqneaze3omLlQCQ=" + }, + "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", + "uri": "https://a.social/notes/782addd9-c051-4eea-8ba4-23d561d0c5bb", + "created_at": "2024-12-01T12:19:06Z", + "author": "https://a.social/users/alice", + "category": "microblog", + "collections": { + "replies": "https://a.social/notes/782addd9-c051-4eea-8ba4-23d561d0c5bb/replies", + "quotes": "https://a.social/notes/782addd9-c051-4eea-8ba4-23d561d0c5bb/quotes" + }, // [!code focus:11] + "content": { + "text/html": { + "content": "Hello, @joe@b.social! How are you doing today?", + "remote": false, + }, + "text/plain": { + "content": "Hello, @joe@b.social! How are you doing today?", + "remote": false, + } + }, + "group": "public", + "mentions": [ // [!code focus:3] + "https://b.social/users/joe" + ] +} +``` + +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 +curl -X POST https://b.social/inbox \ + -H "Content-Type: application/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-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. \ No newline at end of file diff --git a/components/Navigation.tsx b/components/Navigation.tsx index d66a944..06c3c10 100644 --- a/components/Navigation.tsx +++ b/components/Navigation.tsx @@ -268,6 +268,7 @@ export const navigation: NavGroup[] = [ { title: "Validation", href: "/federation/validation" }, { title: "Discovery", href: "/federation/discovery" }, { title: "Delegation", href: "/federation/delegation" }, + { title: "Example", href: "/federation/example" }, ], }, { diff --git a/mdx/rehype.mjs b/mdx/rehype.mjs index bba9407..e736adf 100644 --- a/mdx/rehype.mjs +++ b/mdx/rehype.mjs @@ -31,6 +31,7 @@ const highlighter = await createHighlighter({ "html", "json5", "jsonc", + "markdown", "bash", "php", "python",