Skip to content

Example

The signatures are real. They were made with the example key from Signing Requests, so you can check them by exporting its public key.

This page follows one note from a.example to b.example, from resolving a mention to displaying the note. Notes come from the Social extension, which both instances implement.

Sending a Note

The whole exchange looks like this, and the sections below go through each step.

  1. @alice posts a note that mentions @joe@b.example.
  2. Request: GET /.well-known/webfinger?resource=acct:joe@b.example
    Resolve the mention using WebFinger.
  3. Response:
    Responds with the URL of joe’s profile.
  4. Request: GET /.versia/v0.7/entities/User/034de9r4fr006
    Fetch the user from that URL.
  5. Request: GET /.versia/v0.7/instance
    Verify the signature. b.example fetches a.example’s metadata if it does not have its public key yet.
  6. Response:
    Responds with its InstanceMetadata, which holds the public key.
  7. Response:
    Responds with the User entity.
  8. Request: POST /.versia/v0.7/inbox
    Send the note to the inbox.
  9. Response: 202 Accepted
    Verifies the signature with the key it already has, then responds.

@alice on a.example creates a note with the following content:

Hello, @joe@b.example! How are you doing today?

@alice has mentioned @joe@b.example in the note.

Resolving the Mention

a.example resolves the mention by querying b.example for the user joe using WebFinger.

curl "https://b.example/.well-known/webfinger?resource=acct:joe@b.example" -H "Accept: application/jrd+json"

b.example responds with the following JSON:

{
    "subject": "acct:joe@b.example",
    "links": [
        {
            "rel": "self",
            "type": "application/vnd.versia+json",
            "href": "https://b.example/.versia/v0.7/entities/User/034de9r4fr006"
        }
    ]
}

Fetching the User

a.example fetches the user profile of joe from b.example using the URL provided in the WebFinger response.

# The request is signed by a.example's instance private key
curl https://b.example/.versia/v0.7/entities/User/034de9r4fr006 \
    -H "Accept: application/vnd.versia+json" \
    -H "User-Agent: CoolServer/1.0 (https://coolserver.example)" \
    -H "Versia-Signature: L8icLiUGIu0kznC4Z1DgSMIzAvounZ0fJ7QsJFl+yFxnkb8qRQ/G2IT549f5yGcpfRFJQF87S12dWUM3161jDQ==" \
    -H "Versia-Signed-By: a.example" \
    -H "Versia-Signed-At: 1733051945"

b.example verifies the signature before it answers. That takes a.example’s public key, so if b.example does not have it yet, it reads it from a.example’s metadata:

curl https://a.example/.versia/v0.7/instance -H "Accept: application/vnd.versia+json"

b.example then responds with the following JSON. Its response is signed too, and a.example verifies it the same way, using b.example’s public key.

{
    "id": "034de9r4fr006",
    "type": "User",
    "created_at": "2024-10-13T18:48:19Z",
    "avatar": {
        "url": "https://cdn.example.com/avatars/joe.webp",
        "media_type": "image/webp"
    },
    "display_name": "Joe Swanson (Winter Arc :gigachad:)",
    "username": "joe",
    "manually_approves_subscribers": false,
    "indexable": true,
    "extensions": {
        "pub.versia:custom_emojis": {
            "emojis": [
                {
                    "name": ":gigachad:",
                    "content": {
                        "url": "https://cdn.example.com/emojis/gigachad.png",
                        "media_type": "image/png"
                    }
                }
            ]
        }
    }
}

a.example now has the user profile of joe and can display the note with the correct user information.

Serializing the Note

Finally, a.example serializes the note to send it to joe.

{
    "id": "033r20svqa000",
    "type": "pub.versia:social/Note",
    "created_at": "2024-12-01T11:19:04Z",
    "attachments": [],
    "author": "a.example:034d45r3e6003",
    "category": "microblog",
    "content": "<p>Hello, <a href=\"https://b.example/users/034de9r4fr006\" data-mention=\"b.example:034de9r4fr006\">@joe@b.example</a>! How are you doing today?</p>",
    "visibility": "public",
    "is_sensitive": false,
    "mentions": [
        "b.example:034de9r4fr006"
    ],
    "previews": []
}

It is now time for a.example to send the note to joe.

Sending the Note

a.example sends the note to the inbox of b.example, which hosts joe. note.json holds the note above, with no trailing newline.

# The request is signed by a.example's instance private key
curl -X POST https://b.example/.versia/v0.7/inbox \
    --data-binary @note.json \
    -H "Content-Type: application/vnd.versia+json" \
    -H "User-Agent: CoolServer/1.0 (https://coolserver.example)" \
    -H "Versia-Signature: n+9b5vDR1CcvoxaO+XhWUj6hlVRQeZ+t+EC31SJHD8qHJQ7Y6yd8ZzgrJi9uOWnb7t3ht0EWVa2qF82Jq7ZlCQ==" \
    -H "Versia-Signed-By: a.example" \
    -H "Versia-Signed-At: 1733051946"

b.example verifies the signature with the key it already has, and responds with a 202 Accepted status code.

Displaying the Note

The software on b.example processes the note and shows it to joe using whatever interface it has.

joe can now see the note from @alice on a.example and respond to it.