Miscellaneous fixes (#1110)

* Fix typos in method examples

* Fix broken link to sanitize_config (#1115)

* Add SIDEKIQ_CONCURRENCY

* Fix relref to FilterResult (#1114)

* Fix status_ids and rule_ids description (#1126)

* fix typo (#1129)

* clarify confusing example (mastodon/mastodon#22854)

* fix formatting on linked headings (#1139)

* fix placeholder entity names (#1157)

* better wording

* fix scheduledstatus#params.visibility

* fix missing: tootctl accounts modify --remove-role

* add: status/translate

* add deprecation/removal warning to microformats page

* clarify further how link verification works

* clarify mastodon requirements for webfinger

* fix: push param policy -> data[policy]
This commit is contained in:
trwnh 2023-02-06 19:14:07 -06:00 committed by GitHub
parent 5b0cd8b7f0
commit b458498d25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 237 additions and 44 deletions

View File

@ -4,6 +4,10 @@ description:
menu: menu:
docs: docs:
parent: entities parent: entities
aliases: [
"/api/entities/SOMETHING",
"/api/entities/something",
]
--- ---
## Example ## Example

View File

@ -4,6 +4,10 @@ description:
menu: menu:
docs: docs:
parent: methods parent: methods
aliases: [
"/api/methods/SOMETHING",
"/api/methods/something",
]
--- ---
## What the method does {#anchor} ## What the method does {#anchor}

View File

@ -250,6 +250,10 @@ This variable cannot be defined in dotenv (`.env`) files as it's used before the
{{< page-ref page="admin/scaling" >}} {{< page-ref page="admin/scaling" >}}
#### `SIDEKIQ_CONCURRENCY`
Added in 4.1. Specific to Sidekiq, this variable determines how many different processes Sidekiq forks into. Defaults to `5`.
#### `WEB_CONCURRENCY` #### `WEB_CONCURRENCY`
Specific to Puma, this variable determines how many different processes Puma forks into. Defaults to `2`. Specific to Puma, this variable determines how many different processes Puma forks into. Defaults to `2`.

View File

@ -127,6 +127,9 @@ Modify a user account's role, email, active status, approval mode, or 2FA requir
`--role ROLE` `--role ROLE`
: Define the existing account's custom role by providing the `name` of that [Role]({{< relref "entities/Role" >}}). Default roles include `Owner`, `Admin`, and `Moderator` (case-sensitive). : Define the existing account's custom role by providing the `name` of that [Role]({{< relref "entities/Role" >}}). Default roles include `Owner`, `Admin`, and `Moderator` (case-sensitive).
`--remove-role`
: Removes the current role from the user.
`--email EMAIL` `--email EMAIL`
: Update the user's email address to `EMAIL`. : Update the user's email address to `EMAIL`.
@ -154,7 +157,7 @@ Modify a user account's role, email, active status, approval mode, or 2FA requir
**Version history:**\ **Version history:**\
2.6.0 - added\ 2.6.0 - added\
3.1.2 - added `--reset-password`\ 3.1.2 - added `--reset-password`\
4.0.0 - `--role` no longer takes hard-coded `user`, `moderator`, or `admin` roles. Specify the name of the custom Role instead (case-sensitive). 4.0.0 - `--role` no longer takes hard-coded `user`, `moderator`, or `admin` roles. Specify the name of the custom Role instead (case-sensitive). Remove the current role with `--remove-role`.
--- ---

View File

@ -32,7 +32,7 @@ aliases: [
### `status_id` {#keyword} ### `status_id` {#keyword}
**Description:** The ID of the filtered Status in the database.\ **Description:** The ID of the Status that will be filtered.\
**Type:** String (cast from an integer, but not guaranteed to be a number)\ **Type:** String (cast from an integer, but not guaranteed to be a number)\
**Version history:**\ **Version history:**\
4.0.0 - added 4.0.0 - added

View File

@ -109,14 +109,14 @@ aliases: [
### `status_ids` {#status_ids} ### `status_ids` {#status_ids}
**Description:** The domain name of the instance.\ **Description:** IDs of statuses that have been attached to this report for additional context.\
**Type:** {{<nullable>}} Array of String (cast from integer), or null\ **Type:** {{<nullable>}} Array of String (cast from integer), or null\
**Version history:**\ **Version history:**\
4.0.0 - added 4.0.0 - added
### `rule_ids` {#rule_ids} ### `rule_ids` {#rule_ids}
**Description:** The domain name of the instance.\ **Description:** IDs of the rules that have been cited as a violation by this report.\
**Type:** {{<nullable>}} Array of String (cast from integer), or null\ **Type:** {{<nullable>}} Array of String (cast from integer), or null\
**Version history:**\ **Version history:**\
4.0.0 - added 4.0.0 - added

View File

@ -73,7 +73,7 @@ Returned from `GET /api/v1/scheduled_statuses`:
### `scheduled_at` {#scheduled_at} ### `scheduled_at` {#scheduled_at}
**Description:** ID of the status in the database.\ **Description:** The timestamp for when the status will be posted.\
**Type:** String (ISO 8601 Datetime)\ **Type:** String (ISO 8601 Datetime)\
**Version history:**\ **Version history:**\
2.7.0 - added 2.7.0 - added
@ -150,8 +150,12 @@ Returned from `GET /api/v1/scheduled_statuses`:
#### `params[visibility]` {#params-visibility} #### `params[visibility]` {#params-visibility}
**Description:** The language that will be used for the status.\ **Description:** The visibility that the status will have once it is posted.\
**Type:** {{<nullable>}} String (ISO 639-1 two-letter language code)\ **Type:** String (Enumerable oneOf)\
`public` = Visible to everyone, shown in public timelines.\
`unlisted` = Visible to public, but not included in public timelines.\
`private` = Visible to followers only, and to any mentioned users.\
`direct` = Visible only to mentioned users.\
**Version history:**\ **Version history:**\
2.7.0 - added 2.7.0 - added

View File

@ -334,7 +334,7 @@ aliases: [
### `filtered` {{%optional%}} {#filtered} ### `filtered` {{%optional%}} {#filtered}
**Description:** If the current token has an authorized user: The filter and keywords that matched this status.\ **Description:** If the current token has an authorized user: The filter and keywords that matched this status.\
**Type:** Array of [FilterResult]({{ relref "entities/FilterResult" }})\ **Type:** Array of [FilterResult]({{< relref "entities/FilterResult" >}})\
**Version history:**\ **Version history:**\
4.0.0 - added 4.0.0 - added

View File

@ -0,0 +1,50 @@
---
title: Translation
description: Represents the result of machine translating some status content
menu:
docs:
parent: entities
aliases: [
"/api/entities/Translation",
"/api/entities/translation",
]
---
## Example
```json
{
"content": "<p>Hola mundo</p>",
"detected_source_language": "en",
"provider": "DeepL.com"
}
```
## Attributes
### `content` {#content}
**Description:** The translated text of the status.\
**Type:** String (HTML)\
**Version history:**\
4.0.0 - added
### `detected_source_language` {#detected_source_language}
**Description:** The language of the source text, as auto-detected by the machine translation provider.\
**Type:** String (ISO 639 language code)\
**Version history:**\
4.0.0 - added
### `provider` {#provider}
**Description:** The service that provided the machine translation.
**Type:** String\
**Version history:**\
4.0.0 - added
## See also
{{< page-relref ref="methods/statuses#translate" caption="POST /api/v1/statuses/:id/translate" >}}
{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/translation_serializer.rb" caption="app/serializers/rest/translation_serializer.rb" >}}

View File

@ -505,7 +505,7 @@ Reopen a currently closed report, if it is closed.
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the Report in the database.
##### Headers ##### Headers

View File

@ -106,7 +106,7 @@ Obtain a single filter group owned by the current user.
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the Filter in the database.
##### Headers ##### Headers
@ -1355,7 +1355,7 @@ DELETE /api/v1/filters/:id HTTP/1.1
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the Filter in the database.
##### Headers ##### Headers

View File

@ -466,7 +466,7 @@ The admin has chosen to show domain blocks to no one. The response body is empty
## View extended description {#extended_description} ## View extended description {#extended_description}
```http ```http
GET /api/v1/example HTTP/1.1 GET /api/v1/instance/extended_description HTTP/1.1
``` ```
Obtain an extended description of this server Obtain an extended description of this server

View File

@ -459,7 +459,7 @@ Add accounts to the given list. Note that the user must be following these accou
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the List in the database.
##### Headers ##### Headers
@ -528,7 +528,7 @@ Remove accounts from the given list.
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the List in the database.
##### Headers ##### Headers
@ -561,7 +561,7 @@ Invalid or missing Authorization header.
##### 404: Not found ##### 404: Not found
SOMETHING is not owned by you or does not exist List is not owned by you or does not exist
```json ```json
{ {

View File

@ -44,7 +44,7 @@ timeline[]
#### Response #### Response
##### 200: OK ##### 200: OK
timeline[] = ["home", "notifications"] A request with `?timeline[]=home&timeline[]=notifications`
```json ```json
{ {

View File

@ -175,7 +175,7 @@ Invalid or missing Authorization header.
## Get a single notification {#get-one} ## Get a single notification {#get-one}
```http ```http
GET /api/v1/notification/:id HTTP/1.1 GET /api/v1/notifications/:id HTTP/1.1
``` ```
View information about a notification with a given ID. View information about a notification with a given ID.

View File

@ -102,7 +102,7 @@ client_id
: {{<required>}} String. The client ID, obtained during app registration. : {{<required>}} String. The client ID, obtained during app registration.
client_secret client_secret
: {{<required>}} String. The client secret, obtained durign app registration. : {{<required>}} String. The client secret, obtained during app registration.
redirect_uri redirect_uri
: {{<required>}} String. Set a URI to redirect the user to. If this parameter is set to urn:ietf:wg:oauth:2.0:oob then the token will be shown instead. Must match one of the `redirect_uris` declared during app registration. : {{<required>}} String. Set a URI to redirect the user to. If this parameter is set to urn:ietf:wg:oauth:2.0:oob then the token will be shown instead. Must match one of the `redirect_uris` declared during app registration.

View File

@ -43,7 +43,7 @@ Add a Web Push API subscription to receive notifications. Each access token can
**Version history:**\ **Version history:**\
2.4.0 - added\ 2.4.0 - added\
3.3.0 - added `data[alerts][status]`\ 3.3.0 - added `data[alerts][status]`\
3.4.0 - added `policy`\ 3.4.0 - added `data[policy]`\
3.5.0 - added `data[alerts][update]` and `data[alerts][admin.sign_up]`\ 3.5.0 - added `data[alerts][update]` and `data[alerts][admin.sign_up]`\
4.0.0 - added `data[alerts][admin.report]` 4.0.0 - added `data[alerts][admin.report]`
@ -95,7 +95,7 @@ data[alerts][admin.sign_up]
data[alerts][admin.report] data[alerts][admin.report]
: Boolean. Receive new report notifications? Defaults to false. Must have a role with the appropriate permissions. : Boolean. Receive new report notifications? Defaults to false. Must have a role with the appropriate permissions.
policy data[policy]
: String. Specify whether to receive push notifications from `all`, `followed`, `follower`, or `none` users. : String. Specify whether to receive push notifications from `all`, `followed`, `follower`, or `none` users.
#### Response #### Response

View File

@ -172,7 +172,7 @@ PUT /api/v1/scheduled_statuses/:id HTTP/1.1
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the ScheduledStatus in the database.
##### Headers ##### Headers
@ -253,7 +253,7 @@ DELETE /api/v1/scheduled_statuses/:id HTTP/1.1
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the ScheduledStatus in the database.
##### Headers ##### Headers

View File

@ -523,6 +523,71 @@ Status is private or does not exist
--- ---
## Translate a status {#translate}
```http
POST /api/v1/statuses/:id/translate HTTP/1.1
```
Translate the status content into some language.
**Returns:** [Translation]({{< relref "entities/translation" >}})\
**OAuth:** App token + `read:statuses`\
**Version history:**\
4.0.0 - added
#### Request
##### Path parameters
:id
: {{<required>}} String. The ID of the Status in the database.
##### Form data parameters
lang
: String (ISO 639 language code). The status content will be translated into this language. Defaults to the user's current locale.
##### Headers
Authorization
: {{<required>}} Provide this header with `Bearer <user token>` to gain authorized access to this API method.
#### Response
##### 200: OK
Translating the first "Hello world" post from mastodon.social into Spanish
```json
{
"content": "<p>Hola mundo</p>",
"detected_source_language": "en",
"provider": "DeepL.com"
}
```
##### 404: Not found
Status is private or does not exist
```json
{
"error": "Record not found"
}
```
##### 503: Service unavailable
The translation request failed
```json
{
"error": "Service Unavailable"
}
```
---
## See who boosted a status {#reblogged_by} ## See who boosted a status {#reblogged_by}
```http ```http
@ -1394,7 +1459,7 @@ Edit a given status to change its text, sensitivity, media attachments, or poll.
##### Path parameters ##### Path parameters
:id :id
: {{<required>}} String. The ID of the SOMETHING in the database. : {{<required>}} String. The ID of the Status in the database.
##### Headers ##### Headers

View File

@ -221,7 +221,7 @@ published
## HTML sanitization {#sanitization} ## HTML sanitization {#sanitization}
{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/lib/sanitize_config.rb" caption="app/lib/sanitize_config.rb" >}} {{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/sanitize_ext/sanitize_config.rb" caption="lib/sanitize_ext/sanitize_config.rb" >}}
Mastodon sanitizes incoming HTML in order to not break assumptions for API client developers. Supported elements include `<p>`, `<span>`, `<br>`, and `<a>`. Unsupported elements will be converted to `<p>`.The sanitizer will keep classes if they begin with microformats prefixes or are semantic classes: Mastodon sanitizes incoming HTML in order to not break assumptions for API client developers. Supported elements include `<p>`, `<span>`, `<br>`, and `<a>`. Unsupported elements will be converted to `<p>`.The sanitizer will keep classes if they begin with microformats prefixes or are semantic classes:

View File

@ -7,6 +7,10 @@ menu:
parent: spec parent: spec
--- ---
{{< hint style="info" >}}
As of v4.0.0, HTML permalinks for statuses and profiles have been deprecated and removed from Mastodon. As a result, Microformats are currently not used within nor generated by Mastodon.
{{< /hint >}}
## What are microformats? {#intro} ## What are microformats? {#intro}
[Microformats 2.0](https://microformats.io/) is a standard used to embed metadata directly within an HTML document. Rather than needing to use an API for read-only purposes, a web page can be parsed for certain CSS classes in order to extract information that you have already fetched just by viewing the page, rather than having to separately request that same information from an API. The use of microformats classes allows for semantic parsing of data within a given web page, and can be used to generate feeds, cards, or representations of that data. [Microformats 2.0](https://microformats.io/) is a standard used to embed metadata directly within an HTML document. Rather than needing to use an API for read-only purposes, a web page can be parsed for certain CSS classes in order to extract information that you have already fetched just by viewing the page, rather than having to separately request that same information from an API. The use of microformats classes allows for semantic parsing of data within a given web page, and can be used to generate feeds, cards, or representations of that data.

View File

@ -11,12 +11,6 @@ menu:
The Mastodon API has many methods that require authentication from a client or authorization from a user. This is accomplished with OAuth 2.0, an authorization framework described in [RFC 6749](https://tools.ietf.org/html/rfc6749) that allows third-party applications to obtain limited access to an HTTP service on behalf of a resource owner, through the use of a standardized authorization flow that generates a client access token to be used with HTTP requests. The Mastodon API has many methods that require authentication from a client or authorization from a user. This is accomplished with OAuth 2.0, an authorization framework described in [RFC 6749](https://tools.ietf.org/html/rfc6749) that allows third-party applications to obtain limited access to an HTTP service on behalf of a resource owner, through the use of a standardized authorization flow that generates a client access token to be used with HTTP requests.
Mastodon supports the following OAuth 2 flows:
* **Authorization code flow**: For end-users
* **Password grant flow**: For bots and other single-user applications
* **Client credentials flow**: For applications that do not act on behalf of users
To obtain an OAuth token for a Mastodon website, make sure that you allow your users to specify the domain they want to connect to before login. Use that domain to [acquire a client id/secret]({{< relref "methods/apps#create" >}}) and then [proceed with normal OAuth 2]({{< relref "methods/oauth" >}}). To obtain an OAuth token for a Mastodon website, make sure that you allow your users to specify the domain they want to connect to before login. Use that domain to [acquire a client id/secret]({{< relref "methods/apps#create" >}}) and then [proceed with normal OAuth 2]({{< relref "methods/oauth" >}}).
## OAuth 2 endpoints implemented {#implementation} ## OAuth 2 endpoints implemented {#implementation}
@ -25,17 +19,32 @@ The following descriptions are taken from the [Doorkeeper documentation](https:/
{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/config/initializers/doorkeeper.rb" caption="Doorkeeper config initializer" >}} {{< caption-link url="https://github.com/mastodon/mastodon/blob/master/config/initializers/doorkeeper.rb" caption="Doorkeeper config initializer" >}}
### [GET /oauth/authorize]({{< relref "methods/oauth#authorize" >}}) ### Authorization endpoint (RFC 6749 Section 3.1) {#authorization}
[GET /oauth/authorize]({{% relref "methods/oauth#authorize" %}})
Displays an authorization form to the user. If approved, it will create and return an authorization code, then redirect to the desired `redirect_uri`, or show the authorization code if `urn:ietf:wg:oauth:2.0:oob` was requested. Displays an authorization form to the user. If approved, it will create and return an authorization code, then redirect to the desired `redirect_uri`, or show the authorization code if `urn:ietf:wg:oauth:2.0:oob` was requested.
### [POST /oauth/token]({{< relref "methods/oauth#token" >}}) {#post-oauth-token} ### Token endpoint (RFC 6749 Section 3.2) {#token}
Obtain an access token. This corresponds to the token endpoint, section 3.2 of the OAuth 2 RFC. [POST /oauth/token]({{% relref "methods/oauth#token" %}})
### [POST /oauth/revoke]({{< relref "methods/oauth#revoke" >}}) {#post-oauth-revoke} Obtain an access token. Mastodon supports the following OAuth 2 flows:
Post here with client credentials to revoke an access token. This corresponds to the token endpoint, using the OAuth 2.0 Token Revocation RFC (RFC 7009). Authorization code flow
: For end-users
Password grant flow
: For bots and other single-user applications
Client credentials flow
: For applications that do not act on behalf of users
### Token revocation endpoint (RFC 7009 Section 2) {#revoke}
[POST /oauth/revoke]({{% relref "methods/oauth#revoke" %}})
Post here with client credentials to revoke an access token.
## Common gotchas {#gotchas} ## Common gotchas {#gotchas}

View File

@ -71,5 +71,39 @@ This way, we have translated `@Gargron@mastodon.social` to `https://mastodon.soc
``` ```
{{< /code >}} {{< /code >}}
Note in the above example that `social.example` does not use the same URI structure as Mastodon. Thus, we cannot guess the actor `id` given only the username and domain. However, if `social.example` supports WebFinger, then we can get this `id` by requesting `https://social.example/.well-known/webfinger?resource=acct:username@social.example`and parsing the response for a link with the `application/activity+json` type. Note in the above example that `social.example` does not use the same URI structure as Mastodon. Thus, we cannot guess the actor `id` given only the username and domain. However, if `social.example` supports WebFinger, then we can get this `id` by requesting `https://social.example/.well-known/webfinger?resource=acct:username@social.example`and parsing the response for a link with the `application/ld+json; profile="https://www.w3.org/ns/activitystreams"` or `application/activity+json` type. This link should also have the link relation `rel="self"`.
## Mastodon's requirements for WebFinger
When given an account in the form `username@domain` or `@username@domain`, Mastodon will do the following:
- Construct an `acct:` URI using that username and domain
- Make a WebFinger request for that `resource`
Using that WebFinger response, Mastodon will check the following:
- The `subject` is present
- The `links` array contains a link with `rel` of `self` and `type` of either `application/ld+json; profile="https://www.w3.org/ns/activitystreams"` or `application/activity+json`
- The `href` for this link resolves to an ActivityPub actor
Using that ActivityPub actor representation (which may be provided directly, without the initial WebFinger request), Mastodon will do the following:
- Take `preferredUsername` and the hostname of the actor's server
- Construct an `acct:` URI using that username and domain
- Make a Webfinger request for that `resource`
If the `subject` matches the `resource`, then the process stops here. Otherwise, if the `subject` contains a different canonical account URI, then Mastodon will perform an additional Webfinger request for that canonical account URI in order to ensure that this new `resource` links to the same ActivityPub actor with the same criteria being checked.
In other words, the following cases are valid:
- Asking `example.com` for the resource `acct:alice@example.com` yields a link to an actor on the domain `example.com` with a `preferredUsername` of `alice`, and the `subject` matches the requested resource `acct:alice@example.com`
- Asking `example.com` for the resource `acct:alice@example.com` yields a link to an actor on the domain `ap.example.com` with a `preferredUsername` of `alice`
- ...then, asking `ap.example.com` for the resource `acct:alice@ap.example.com` yields a `subject` of `acct:alice@example.com` and a link to the same actor
## See also
{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/services/activitypub/fetch_remote_actor_service.rb" caption="app/services/activitypub/fetch_remote_actor_service.rb" >}}
{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/services/resolve_account_service.rb" caption="app/services/resolve_account_service.rb" >}}
{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/lib/webfinger.rb" caption="app/lib/webfinger.rb" >}}

View File

@ -65,7 +65,7 @@ Its completely up to you what you put there. The content can contain mentions
### Link verification {#verification} ### Link verification {#verification}
Document-based verification and blue ticks are not possible without a central authority. However, Mastodon can cross-reference the links you put on your profile to prove that you are the real owner of those links. In case one of those links is your personal homepage that is known and trusted, it can serve as the next-best-thing to identity verification. Document-based verification and blue ticks are not possible without a central authority. However, Mastodon can cross-reference the links you put on your profile to prove that you are the real owner of those links. In case one of those links is your personal homepage that is known and trusted, it can serve as the next-best-thing to identity document verification.
{{< hint style="info" >}} {{< hint style="info" >}}
Because Mastodon can be self-hosted, there is no better way to verify your identity than to host Mastodon on your own domain, which people already trust. Because Mastodon can be self-hosted, there is no better way to verify your identity than to host Mastodon on your own domain, which people already trust.
@ -77,14 +77,26 @@ If you put an HTTPS link in your profile metadata, Mastodon checks if that link
<a href="https://social.example.com/@username" rel="me">Follow me on Mastodon!</a> <a href="https://social.example.com/@username" rel="me">Follow me on Mastodon!</a>
``` ```
More precisely, Mastodon will validate the link under the following conditions: It may also be embedded directly in the head of your web page:
- It is not within an `iframe` (note that some "block-based" CMS software may wrap block elements within iframes)
- Since 4.0: the hostname does not change after IDN normalization
- it starts with HTTPS
- the resolved page contains at least one `a` or `link` tag with a `rel="me"`
- the `href` attribute on one of those elements is equal to the URL for your Mastodon profile
Alternatively, validation will occur if the resolved page's *first* link has an `href` value that redirects to your Mastodon profile's URL (such as through a link shortener). ```html
<link href="https://social.example.com/@username" rel="me">
```
#### Validation criteria for verified links
Mastodon will validate the "verified link" profile field under the following conditions:
- The value of the profile field is **a link that MUST start with HTTPS**. Other values will not be processed. Other types of links are not processed. In particular, plain HTTP links are not processed because they are insecure and may be modified by an attacker in transit.
- Since 4.0: **the hostname must not change after IDN normalization**. This is to prevent homeograph attacks where some Unicode character is used that looks confusingly similar to some ASCII character in a mixed string. For example, `U+0061` (English lowercase "a") and `U+0430` (Cyrillic small letter "а") appear nearly identical, but they are completely different characters. This similarity in appearance could be used maliciously to mislead people.
Mastodon will then resolve the link and fetch the web page located there, looking for a qualified link that matches the criteria:
- The resolved page must contain at least one `a` or `link` tag with a `rel="me"` attribute.
- The `href` attribute on one of those elements must be equal to the URL for your Mastodon profile.
- If no links with `rel="me"` are found, Mastodon will look for the *first* link, and the `href` value must redirect to your Mastodon profile's URL. (This provides limited support for web pages that use link shorteners and do not use rel-me.)
**Any such link must not be within an `iframe`**. An `iframe` effectively means the link is no longer on the same web page, but rather it is on some external web page which is being embedded in the current one. (Note that some "block-based" CMS software may wrap block elements within iframes, which prevents verification for this reason.)
{{< hint style="info" >}} {{< hint style="info" >}}
Make sure to save your profile *after* adding the rel-me link to your web page! The verification process is triggered when you save your profile, and may take some time before completing. If you have added the rel-me link and verification is not working, then try deleting the link, saving, re-adding the link, and saving again. Make sure to save your profile *after* adding the rel-me link to your web page! The verification process is triggered when you save your profile, and may take some time before completing. If you have added the rel-me link and verification is not working, then try deleting the link, saving, re-adding the link, and saving again.