From 05eda169fe88f712c21f61daed1ae025cc1091e7 Mon Sep 17 00:00:00 2001 From: Oneric Date: Thu, 16 May 2024 21:05:01 +0200 Subject: [PATCH] Document AP and nodeinfo extensions And while add it point to this via a top-level FEDERATION.md document as standardised by FEP-67ff. Also add a few missing descriptions to the config cheatsheet and move the recently removed C2S extension into an appropiate subsection. --- CHANGELOG.md | 3 + FEDERATION.md | 42 +++ docs/docs/configuration/cheatsheet.md | 2 + docs/docs/development/ap_extensions.md | 279 ++++++++++++++++++- docs/docs/development/nodeinfo_extensions.md | 141 ++++++++++ 5 files changed, 463 insertions(+), 4 deletions(-) create mode 100644 FEDERATION.md create mode 100644 docs/docs/development/nodeinfo_extensions.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e317c96b..2d3034464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Issue allowing use of non-media objects as attachments and crashing timeline rendering - Issue allowing webfinger spoofing in certain situations +## Added +- Implement [FEP-67ff](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md) (federation documentation) + ## 2024.04 ## Added diff --git a/FEDERATION.md b/FEDERATION.md new file mode 100644 index 000000000..4524ec4e2 --- /dev/null +++ b/FEDERATION.md @@ -0,0 +1,42 @@ +# Federation + +## Supported federation protocols and standards + +- [ActivityPub](https://www.w3.org/TR/activitypub/) (Server-to-Server) +- [WebFinger](https://webfinger.net/) +- [Http Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures) +- [NodeInfo](https://nodeinfo.diaspora.software/) + +## Supported FEPs + +- [FEP-67ff: FEDERATION](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md) +- [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md) +- [FEP-fffd: Proxy Objects](https://codeberg.org/fediverse/fep/src/branch/main/fep/fffd/fep-fffd.md) + +## ActivityPub + +Akkoma mostly follows the server-to-server parts of the ActivityPub standard, +but implements quirks for Mastodon compatibility as well as Mastodon-specific +and custom extensions. + +See our documentation and Mastodon’s federation information +linked further below for details on these quirks and extensions. + +Akkoma does not perform JSON-LD processing. + +### Required extensions + +#### HTTP Signatures +All AP S2S POST requests to Akkoma instances MUST be signed. +Depending on instance configuration the same may be true for GET requests. + +## Nodeinfo + +Akkoma provides many additional entries in its nodeinfo response, +see the documentation linked below for details. + +## Additional documentation + +- [Akkoma’s ActivityPub extensions](https://docs.akkoma.dev/develop/development/ap_extensions/) +- [Akkoma’s nodeinfo extensions](https://docs.akkoma.dev/develop/development/nodeinfo_extensions/) +- [Mastodon’s federation requirements](https://github.com/mastodon/mastodon/blob/main/FEDERATION.md) diff --git a/docs/docs/configuration/cheatsheet.md b/docs/docs/configuration/cheatsheet.md index 3b5dfa41e..3591d2459 100644 --- a/docs/docs/configuration/cheatsheet.md +++ b/docs/docs/configuration/cheatsheet.md @@ -63,6 +63,8 @@ To add configuration to your config file, you can copy it from the base config. * `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `["example.com"]`, (default: `[]`) * `languages`: List of Language Codes used by the instance. This is used to try and set a default language from the frontend. It will try and find the first match between the languages set here and the user's browser languages. It will default to the first language in this setting if there is no match.. (default `["en"]`) * `export_prometheus_metrics`: Enable prometheus metrics, served at `/api/v1/akkoma/metrics`, requiring the `admin:metrics` oauth scope. +* `privileged_staff`: Set to `true` to give moderators access to a few higher responsibility actions. +* `federated_timeline_available`: Set to `false` to remove access to the federated timeline for all users. ## :database * `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes). diff --git a/docs/docs/development/ap_extensions.md b/docs/docs/development/ap_extensions.md index bf9420272..a5e3d0186 100644 --- a/docs/docs/development/ap_extensions.md +++ b/docs/docs/development/ap_extensions.md @@ -4,7 +4,6 @@ The following endpoints are additionally present into our actors. - `oauthRegistrationEndpoint` (`http://litepub.social/ns#oauthRegistrationEndpoint`) -- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`) ### oauthRegistrationEndpoint @@ -12,6 +11,279 @@ Points to MastodonAPI `/api/v1/apps` for now. See +## Emoji reactions + +Emoji reactions are implemented as a new activity type `EmojiReact`. +A single user is allowed to react multiple times with different emoji to the +same post. However, they may only react at most once with the same emoji. +Repeated reaction from the same user with the same emoji are to be ignored. +Emoji reactions are also distinct from `Like` activities and a user may both +`Like` and react to a post. + +!!! note + Misskey also supports emoji reactions, but the implementations differs. + It equates likes and reactions and only allows a single reaction per post. + +The emoji is placed in the `content` field of the activity +and the `object` property points to the note reacting to. + +Emoji can either be any Unicode emoji sequence or a custom emoji. +The latter must place their shortcode, including enclosing colons, +into `content` and put the emoji object inside the `tag` property. +The `tag` property MAY be omitted for Unicode emoji. + +An example reaction with a Unicode emoji: +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://example.org/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "type": "EmojiReact", + "id": "https://example.org/activities/23143872a0346141", + "actor": "https://example.org/users/akko", + "nickname": "akko", + "to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"], + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "content": "🧡", + "object": "https://remote.example/objects/9f0e93499d8314a9" +} +``` + +An example reaction with a custom emoji: +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://example.org/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "type": "EmojiReact", + "id": "https://example.org/activities/d75586dec0541650", + "actor": "https://example.org/users/akko", + "nickname": "akko", + "to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"], + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "content": ":mouse:", + "object": "https://remote.example/objects/9f0e93499d8314a9", + "tag": [{ + "type": "Emoji", + "id": null, + "name": "mouse", + "icon": { + "type": "Image", + "url": "https://example.org/emoji/mouse/mouse.png" + } + }] +} +``` + +!!! note + Although an emoji reaction can only contain a single emoji, + for compatibility with older versions of Pleroma and Akkoma, + it is recommended to wrap the emoji object in a single-element array. + +When reacting with a remote custom emoji do not include the remote domain in `content`’s shortcode +*(unlike in our REST API which needs the domain)*: +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://example.org/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "type": "EmojiReact", + "id": "https://example.org/activities/7993dcae98d8d5ec", + "actor": "https://example.org/users/akko", + "nickname": "akko", + "to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"], + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "content": ":hug:", + "object": "https://remote.example/objects/9f0e93499d8314a9", + "tag": [{ + "type": "Emoji", + "id": "https://other.example/emojis/hug", + "name": "hug", + "icon": { + "type": "Image", + "url": "https://other.example/files/b71cea432b3fad67.webp" + } + }] +} +``` + +Emoji reactions can be retracted using a standard `Undo` activity: +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "http://example.org/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "type": "Undo", + "id": "http://example.org/activities/4685792e-efb6-4309-b508-ae4f355dd695", + "actor": "https://example.org/users/akko", + "to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"], + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "object": "https://example.org/activities/23143872a0346141" +} +``` + +## User profile backgrounds + +Akkoma federates user profile backgrounds the same way as Sharkey. + +An actors ActivityPub representation contains an additional +`backgroundUrl` property containing an `Image` object. This property +belongs to the `"sharkey": "https://joinsharkey.org/ns#"` namespace. + +## Quote Posts + +Akkoma allows referencing a single other note as a quote, +which will be prominently displayed in the interface. + +The quoted post is referenced by its ActivityPub id in the `quoteUri` property. + +!!! note + Old Misskey only understood and modern Misskey still prefers + the `_misskey_quote` property for this. Similar some other older + software used `quoteUrl` or `quoteURL`. + All current implementations with quote support understand `quoteUri`. + +Example: +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://example.org/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "type": "Note", + "id": "https://example.org/activities/85717e587f95d5c0", + "actor": "https://example.org/users/akko", + "to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"], + "cc": ["https://www.w3.org/ns/activitystreams#Public"], + "context": "https://example.org/contexts/1", + "content": "Look at that!", + "quoteUri": "http://remote.example/status/85717e587f95d5c0", + "contentMap": { + "en": "Look at that!" + }, + "source": { + "content": "Look at that!", + "mediaType": "text/plain" + }, + "published": "2024-04-06T23:40:28Z", + "updated": "2024-04-06T23:40:28Z", + "attachemnt": [], + "tag": [] +} +``` + +## Threads + +Akkoma assigns all posts of the same thread the same `context`. This is a +standard ActivityPub property but its meaning is left vague. Akkoma will +always treat posts with identical `context` as part of the same thread. + +`context` must not be assumed to hold any meaning or be dereferencable. + +Incoming posts without `context` will be assigned a new context. + +!!! note + Mastodon uses the non-standard `conversation` property for the same purpose + *(named after an older OStatus property)*. For incoming posts without + `context` but with `converstions` Akkoma will use the value from + `conversations` to fill in `context`. + For outgoing posts Akkoma will duplicate the context into `conversation`. + +## Post Source + +Unlike Mastodon, Akkoma supports drafting posts in multiple source formats +besides plaintext, like Markdown or MFM. The original input is preserved +in the standard ActivityPub `source` property *(not supported by Mastodon)*. +Still, `content` will always be present and contain the prerendered HTML form. + +Supported `mediaType` include: +- `text/plain` +- `text/markdown` +- `text/bbcode` +- `text/x.misskeymarkdown` + +## Post Language + +!!! note + This is also supported in and compatible with Mastodon, but since + joinmastodon.org doesn’t document it yet it is included here. + [GoToSocial](https://docs.gotosocial.org/en/latest/federation/federating_with_gotosocial/#content-contentmap-and-language) + has a more refined version of this which can correctly deal with multiple language entries. + +A post can indicate its language by including a `contentMap` object +which contains a sub key named after the language’s ISO 639-1 code +and it’s content identical to the post’s `content` field. + +Currently Akkoma, just like Mastodon, only properly supports a single language entry, +in case of multiple entries a random language will be picked. +Furthermore, Akkoma currently only reads the `content` field +and never the value from `contentMap`. + +## Local post scope + +Post using this scope will never federate to other servers +but for the sake of completeness it is listed here. + +In addition to the usual scopes *(public, unlisted, followers-only, direct)* +Akkoma supports an “unlisted” post scope. Such posts will not federate to +other instances and only be shown to logged-in users on the same instance. +It is included into the local timeline. +This may be useful to discuss or announce instance-specific policies and topics. + +A post is addressed to the local scope by including `/#Public` +in its `to` field. E.g. if the instance is on `https://example.org` it would use +`https://example.org/#Public`. + +An implementation creating a new post MUST NOT address both the local and +general public scope `as:Public` at the same time. A post addressing the local +scope MUST NOT be sent to other instances or be possible to fetch by other +instances regardless of potential other listed addressees. + +When receiving a remote post addressing both the public scope and what appears +to be a local-scope identifier, the post SHOULD be treated without assigning any +special meaning to the potential local-scope identifier. + +!!! note + Misskey-derivatives have a similar concept of non-federated posts, + however those are also shown publicly on the local web interface + and are thus visible to non-members. + +## List post scope + +Messages originally addressed to a custom list will contain +a `listMessage` field with an unresolvable pseudo ActivityPub id. + +# Deprecated and Removed Extensions + +The following extensions were used in the past but have been dropped. +Documentation is retained here as a reference and since old objects might +still contains related fields. + +## Actor endpoints + +The following endpoints used to be present: + +- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`) + ### uploadMedia Inspired by , it is part of the ActivityStreams namespace because it used to be part of the ActivityPub specification and got removed from it. @@ -20,9 +292,8 @@ Content-Type: multipart/form-data Parameters: - (required) `file`: The file being uploaded -- (optionnal) `description`: A plain-text description of the media, for accessibility purposes. +- (optional) `description`: A plain-text description of the media, for accessibility purposes. Response: HTTP 201 Created with the object into the body, no `Location` header provided as it doesn't have an `id` -The object given in the reponse should then be inserted into an Object's `attachment` field. - +The object given in the response should then be inserted into an Object's `attachment` field. diff --git a/docs/docs/development/nodeinfo_extensions.md b/docs/docs/development/nodeinfo_extensions.md new file mode 100644 index 000000000..3edc31524 --- /dev/null +++ b/docs/docs/development/nodeinfo_extensions.md @@ -0,0 +1,141 @@ +# Nodeinfo Extensions + +Akkoma currently implements version 2.0 and 2.1 of nodeinfo spec, +but provides the following additional fields. + +## metadata + +The spec leaves the content of `metadata` up to implementations +and indeed Akkoma adds many fields here apart from the commonly +found `nodeName` and `nodeDescription` fields. + +### accountActivationRequired +Whether or not users need to confirm their email before completing registration. +*(boolean)* + +!!! note + Not to be confused with account approval, where each registration needs to + be manually approved by an admin. Account approval has no nodeinfo entry. + +### features + +Array of strings denoting supported server features. E.g. a server supporting +quote posts should include a `"quote_posting"` entry here. + +A non-exhaustive list of possible features: +- `polls` +- `quote_posting` +- `editing` +- `bubble_timeline` +- `pleroma_emoji_reactions` *(Unicode emoji)* +- `custom_emoji_reactions` +- `akkoma_api` +- `akkoma:machine_translation` +- `mastodon_api` +- `pleroma_api` + +### federatedTimelineAvailable +Whether or not the “federated timeline”, i.e. a timeline containing posts from +the entire known network, is made available. +*(boolean)* + +### federation +This section is optional and can contain various custom keys describing federation policies. +The following are required to be presented: +- `enabled` *(boolean)* whether the server federates at all + +A non-exhaustive list of optional keys: +- `exclusions` *(boolean)* whether some federation policies are withheld +- `mrf_simple` *(object)* describes how the Simple MRF policy is configured + +### fieldsLimits +A JSON object documenting restriction for user account info fields. +All properties are integers. + +- `maxFields` maximum number of account info fields local users can create +- `maxRemoteFields` maximum number of account info fields remote users can have + before the user gets rejected or fields truncated +- `nameLength` maximum length of a field’s name +- `valueLength` maximum length of a field’s value + +### invitesEnabled +Whether or not signing up via invite codes is possible. +*(boolean)* + +### localBubbleInstances +Array of domains (as strings) of other instances chosen +by the admin which are shown in the bubble timeline. + +### mailerEnabled +Whether or not the instance can send out emails. +*(boolean)* + +### nodeDescription +Human-friendly description of this instance +*(string)* + +### nodeName +Human-friendly name of this instance +*(string)* + +### pollLimits +JSON object containing limits for polls created by local users. +All values are integers. +- `max_options` maximum number of poll options +- `max_option_chars` maximum characters per poll option +- `min_expiration` minimum time in seconds a poll must be open for +- `max_expiration` maximum time a poll is allowed to be open for + +### postFormats +Array of strings containing media types for supported post source formats. +A non-exhaustive list of possible values: +- `text/plain` +- `text/markdown` +- `text/bbcode` +- `text/x.misskeymarkdown` + +### private +Whether or not unauthenticated API access is permitted. +*(boolean)* + +### privilegedStaff +Whether or not moderators are trusted to perform some +additional tasks like e.g. issuing password reset emails. + +### publicTimelineVisibility +JSON object containing boolean-valued keys reporting +if a given timeline can be viewed without login. +- `local` +- `federated` +- `bubble` + +### restrictedNicknames +Array of strings listing nicknames forbidden to be used during signup. + +### skipThreadContainment +Whether broken threads are filtered out +*(boolean)* + +### staffAccounts +Array containing ActivityPub IDs of local accounts +with some form of elevated privilege on the instance. + +### suggestions +JSON object containing info on whether the interaction-based +Mastodon `/api/v1/suggestions` feature is enabled and optionally +additional implementation-defined fields with more details +on e.g. how suggested users are selected. + +!!! note + This has no relation to the newer /api/v2/suggestions API + which also (or exclusively) contains staff-curated entries. + +- `enabled` *(boolean)* whether or not user recommendations are enabled + +### uploadLimits +JSON object documenting various upload-related size limits. +All values are integers and in bytes. +- `avatar` maximum size of uploaded user avatars +- `banner` maximum size of uploaded user profile banners +- `background` maximum size of uploaded user profile backgrounds +- `general` maximum size for all other kinds of uploads