Skip to content

Changelog

Changes to the protocol since Working Draft 3, newest first. Versions used to be called Working Drafts: Working Draft X is version 0.X.

v0.7.0

All endpoints moved from /.versia/v0.6/ to /.versia/v0.7/. This version also includes the changes previously listed as v0.6.1.

The core is now general-purpose. Posts moved to the Social extension, Follow became a generic Subscribe, and User keeps only the fields every instance needs.

Versions

Signatures and Identity

  • The signed string is now $method $host $path?query $timestamp $bodyhash, on one line with parts separated by single spaces. It now covers the target host and the query string.
  • Signing inputs are now fully defined: bytes as sent, a hash over the raw body, and standard padded base64.
  • Listed which routes require signatures, in both directions. The well-known endpoint, WebFinger and Instance Metadata are exempt.
  • Only 2xx responses have to verify. A response whose signature fails, or that is signed by an instance other than the one asked, is discarded and counts as a failed attempt.
  • Receivers ignore a Versia-Signature they have already accepted from the same instance on the inbox, and answer it with a 2xx. Processing must be idempotent.
  • Defined key rotation: an instance has one current key, and a receiver fetches the signer’s metadata again before rejecting a signature that fails to verify.
  • An entity’s author must belong to the instance that signed the message, whether it was delivered or fetched. This also covers each entity in a Collection’s items.
  • References must always be domain:id, using a Domain as their prefix, so they cannot be bare IDs or contain port numbers or IP addresses. Their definition moved to Entities.
  • Domains are now defined by RFC 1123 and IDNA, and must be lowercase.
  • Domains cannot be IP addresses or single labels.
  • User addresses now only allow usernames.

Data Model

  • All JSON must now be an I-JSON message. Numeric fields are now integer, a whole number up to 2^53-1 in any notation, or float. These replace u64 and f64.
  • An entity with a \r\n line break inside a string value is now invalid.
  • Strings and arrays may be empty unless their field says otherwise. id, username and a Reaction’s content have at least 1 character.
  • Senders may now omit optional fields instead of setting them to null. Both mean the same, and receivers must accept both.
  • Entities, structures and transient entities now have definitions. Transient entities have no id.
  • An id is now 1 to 13 characters of a-z, 0-9, - and _, so UUIDs and uppercase letters are no longer valid. Ids are opaque: receivers must not read a time or an order from them. Choosing IDs shows one way to make short ids.
  • Added updated_at and defined how an entity changes: the whole entity is sent again with the same id, and id, type, author and created_at cannot change.
  • Removed $schema from entities, and author from Collections and Reference Collections.
  • ContentFormat is replaced by two types. RichText is one HTML string, and other text formats are gone. Media is one file, with its url and media_type in the same object, and no other versions of the file.
  • RichText defines the elements and attributes senders can use, a fallback for each, and how to turn it into plain text. Mentions are links with a data-mention attribute, and extensions can add data- attributes to span and div.
  • Media’s hash is a lowercase hexadecimal string, and thumbhash is standard padded base64. Senders should include size and hash, and thumbhash, width and height for images and video.
  • Renamed URI Collection to Reference Collection. A Collection only holds entities authored on the instance serving it.
  • Note: quotes is now quoting, and the new required visibility field replaces the group field’s visibility values. Groups moved to the Groups extension. An unknown category is treated as absent.
  • User, Note and Group have a new optional url, the address of their page on their instance.
  • Removed the Types page. RFC3339 and URI types now link directly to their RFCs.

Federation

  • Delivery and access are now defined per entity type. Instances answer 404 for entities the requester may not fetch, and subscribers and mentions Notes also reach the authors of the Notes they reply to.
  • Delivery has no guaranteed order. Receivers should fetch missing references, and ignore entities with nothing to apply to.
  • Receivers must ignore unknown fields. An unknown namespaced type is answered with a 2xx and dropped, and an unknown core type is rejected.
  • Added a status code table and an optional error body.
  • Rate limits now use 429 and Retry-After, replacing the expired IETF draft headers. Added retry rules.
  • Defined media types per route. Content-Type is now application/vnd.versia+json, with charset=utf-8 optional, and the inbox no longer needs an Accept header.
  • Follow, FollowAccept, FollowReject and Unfollow are replaced by Subscribe, SubscribeAccept, SubscribeReject and Unsubscribe, which also replace the four subscription entities of Groups. They carry subscriber, target and target_type instead of an author, and receivers check the sender against subscriber or target. They can target any type whose page says it accepts subscriptions. The target’s instance must answer every Subscribe, with no time limit on a pending request, Unsubscribe cancels a pending request, and SubscribeReject also removes an existing subscriber.
  • Followers are now called subscribers. User followers, following and manually_approves_followers are now subscribers, subscriptions and manually_approves_subscribers, and the Note visibility value followers is now subscribers.
  • Delete: author is optional, instances can delete, and the instance hosting the entity must send the Delete. A Delete whose deleted_type differs from the stored type is ignored. Added 410 and 404 for fetching deleted entities.
  • A user’s Notes collection holds the Notes they have authored.
  • Added an endpoint for instance collections, since Instance Metadata has no id, and defined pagination.
  • Extension endpoints must be under /.versia/v0.X/extensions/{extension_name}/. Extensions now state their own delivery and access rules.
  • Entity types and collection names in paths are no longer percent-encoded, as in /entities/pub.versia:groups/Group/1234.
  • Entity types in URLs are always capitalized, and examples no longer have port numbers in domains.

Extensions

  • Extensions now have versions, listed in compatibility.extensions as pub.versia:likes@1. Instances use the highest version they have in common.
  • Extension values must be JSON objects, namespaces must come from a domain the author controls, and pub.versia is reserved.
  • An extension can now require other extensions at a version. An instance that lists it must also list what it requires, and instances leave out its data unless they share those too. Extensions may still never be required to process core entities.
  • Notes moved out of the core into the new Social extension (pub.versia:social), so the core has no post type. A Note’s type is now pub.versia:social/Note, and a bare Note is an unknown core type that receivers reject. The extension also holds visibility, mentions, replies and quotes, the delivery and access rules for Notes, and the notes, reply, quote and compose Versia Link actions.
  • User outbox and featured are now the Social collections pub.versia:social/Notes and pub.versia:social/Featured. The outbox is renamed because it only ever held Notes.
  • User bio and fields moved to Profile, where fields is optional, and header moved to Decorations.
  • Likes, Share, Reactions, Polls, Groups and Interaction Controls now require Social. The share and groups Versia Link actions moved to Share and Groups.
  • All entities that act on another entity (Like, Dislike, Share, Reaction and Vote) now use target.
  • The Vanity extension (pub.versia:vanity) is split in two. Profile (pub.versia:profile) holds pronouns, birthday, location and timezone, and Decorations (pub.versia:decorations) holds avatar_overlays, avatar_mask, background and audio. aliases is removed.
  • Profile: pronouns is plain text for each BCP 47 language tag, replacing the five pronoun forms. birthday is YYYY-MM-DD, or --MM-DD without the year, replacing the 0000 year. Whether a well-formed date is plausible is up to implementations. location is plain text only, and timezone must be an IANA time zone name.
  • Decorations: each field must hold the media type it names: images, or audio for audio.
  • Delegation: now only changes who an action is shown as. It grants no rights over the other user’s entities. A delegator that cannot be fetched counts as not listing the delegate.
  • Added the Domain Handles extension (pub.versia:domain_handles). A user can be shown by a domain they own, such as @joe.example, once a DNS TXT record at _versia.joe.example, or a file at https://joe.example/.well-known/versia-handle, names their Reference. The DNS record decides when both exist.
  • Groups: members join through Subscribe, and the GroupSubscribe, GroupUnsubscribe, GroupSubscribeAccept and GroupSubscribeReject entities are removed. Defined moderators and federation, and GroupFederate.note is now target. The instance of a Note’s author sends the Note to the group’s instance, which sends a GroupFederate to its members’ instances, and those fetch the Note. name is a plain string, and description is RichText.
  • Instance Messaging: named pub.versia:instance_messaging so that instances can list it in compatibility.extensions, moved to the extensions path, and defined as unsigned with untrusted text. The body should name the sender.
  • Interaction Controls: only allowed remains, priorities are gone, keys are lowercase interaction types that each extension registers (such as pub.versia:social/reply and pub.versia:likes/like, replacing reply, quote and entity type names), and the InteractionGroup values now match their descriptions (followed is gone, mentioned is added, and followers and following are now subscribers and subscriptions). A refused interaction must be rejected. allowed never widens who can see a Note.
  • Migration: new is now destination, and previous is optional. Migrations have defined delivery, and subscriptions move through normal Subscribes. The old account should stay served for at least 30 days.
  • Polls: options is now a list of objects holding content and votes. A Vote now holds all of a user’s choices in an options list of indexes, replacing one Vote per option with option, and a user has at most one Vote in each poll. A Vote can change by being sent again, and is withdrawn by a Delete, but its target cannot change. A refused Vote must be rejected.
  • Reactions: defined how content holds a custom emoji, and a Reaction is undone with a Delete.
  • Reports: reported is now a list of objects holding type and reference.
  • Share: only a public Note can be shared.
  • Versia Links: instance is required except for compose, usernames are allowed, {text} is percent-encoded, and extensions can add actions.

Documentation

  • Requirements now use BCP 14 key words, only in bold capitals. Some that were only written in lowercase now use them, including undoing a Like or Dislike with a Delete and the limits on remote data in Validation.
  • Defined conformance.
  • The Security Model now says what signatures do not prove and where to report a vulnerability. Validation adds rules for remote HTML and outbound requests.
  • Restructured the site. /api/basics was split between Versions, HTTP and Entities, /api/endpoints moved to Endpoints, /philosophy/principles moved to Principles, /federation/validation moved to Validation, and /federation/example moved to Example. The old URLs redirect, without their fragments. Entity and extension pages now share one layout, so their section anchors changed.
  • Added Other Protocols, which maps Versia’s concepts to ActivityPub and Matrix.
  • Added a Glossary and guides on signing requests and implementing a receiver.
  • Removed repeated statements of rules, which now link to the page that defines them, along with promotional text. No rule changed.
  • Corrected samples across the specification, including the federation example and its signatures.

v0.6.0

  • Added a media type for Versia entities: application/vnd.versia+json.
  • Overhauled the instance-to-instance API. Its endpoints are now defined and namespaced under /.versia/v0.6/, instead of each implementation choosing its own.
  • Removed per-user key pairs. Only the instance key pair is used for signing.
  • Moved Delegation to an extension.
  • The well-known endpoint now only lists supported protocol versions.
  • Changed how entities refer to each other:
    • Removed the uri field from all entities.
    • Every field that held a URI now holds that entity’s id.
    • Added endpoints under /.versia/v0.6/ to fetch entities by their id.
  • Added References as a way to refer to other entities.
  • Replaced null authors with an optional field.
  • Replaced the hash field on ContentFormat with a hex-encoded SHA-256 hash.
  • Added more guidance on handling JSON.
  • Clarified what a Domain means in Versia.
  • Note: is_sensitive, attachments, mentions and previews are now required.
  • User: fields, manually_approves_followers and indexable are now required.
  • Vanity: removed the short pronoun form, since union types are hard to parse in strongly typed languages.

v0.5.0

  • Reports: removed the URI, and replaced reason with tags.
  • The docs now use the term “transient entity” where it applies.
  • Text fields must use Unix-style \n line endings.
  • Renamed two headers, following RFC 6648:
    • X-Signature to Versia-Signature
    • X-Signed-By to Versia-Signed-By
  • Replaced the nonce in the signature system with a timestamp, sent as Versia-Signed-At.
  • Rate limits now follow IETF draft-polli-ratelimit-headers-02.
  • Moved Groups to an extension, as they were getting too complex for the core protocol, and documented how groups federate and how subscribing works. The Note group field’s description changed to match.
  • Added Versia Links.
  • Timestamps now use RFC 3339 instead of ISO 8601. The two are mostly interchangeable, and RFC 3339 is stricter, so most implementations should not need to change anything.
  • Added an optional $schema field to Entities.
  • Added the Interaction Controls extension.
  • Added URI Collections, now called Reference Collections. Every Collection that can contain remote entities now uses one, such as User collections.followers.
  • Added a collections field to Notes. The Likes, Reactions and Share extensions now use it, instead of a field in extensions.
  • User username may now contain uppercase letters, and is case-insensitive.
  • Vanity: added timezone.

v0.4.0

  • Rewrote the signature system from scratch, to be simpler and not depend on dates.
  • Moved Likes and Dislikes to an extension.
  • Added Delegation.
  • Renamed fields on several common entities, such as Users and Notes.
  • Removed the Patch entity, since edits can be sent to inboxes directly.
  • A uri no longer has to contain the entity’s id, and uri is optional on some entities that are not worth keeping track of.
  • id can now be any string, not only a UUID.
  • Added shared inboxes.
  • ContentFormat: added remote.
  • Switched from BlurHash to ThumbHash.
  • Custom Emojis: added identification characters.
  • Users: added manually_approves_followers.
  • Notes: removed visibility, and subject is now optional.
  • Clarified how Follows work.
  • Renamed Undo to Delete, which is now only used to delete entities.
  • Added the Unfollow entity.
  • Reworked ServerMetadata and renamed it to InstanceMetadata.
  • Removed server actors, and moved instance public keys to InstanceMetadata.
  • Added algorithm to the public keys of Users and InstanceMetadata, for future use. Only ed25519 is allowed. The second public_key is renamed to key.
  • Renamed Announce to Share.
  • Collections: renamed prev to previous.
  • Removed extension_type from entities, which now use the type field instead.
  • Polls: removed VoteResult.