diff --git a/.github/workflows/rebase-needed.yml b/.github/workflows/rebase-needed.yml new file mode 100644 index 00000000..05a1deab --- /dev/null +++ b/.github/workflows/rebase-needed.yml @@ -0,0 +1,27 @@ +name: PR Needs Rebase + +on: + schedule: + - cron: '0 * * * *' + +permissions: + pull-requests: write + +jobs: + label-rebase-needed: + runs-on: ubuntu-latest + + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Check for merge conflicts + uses: eps1lon/actions-label-merge-conflict@releases/2.x + with: + dirtyLabel: 'rebase needed :construction:' + repoToken: '${{ secrets.GITHUB_TOKEN }}' + commentOnClean: This pull request has resolved merge conflicts and is ready for review. + commentOnDirty: This pull request has merge conflicts that must be resolved before it can be merged. + retryMax: 30 + continueOnMissingPermissions: false \ No newline at end of file diff --git a/README.md b/README.md index 07de0046..64eb826b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -![Mastodon](https://i.imgur.com/NhZc40l.png) -==== +

+ + + Mastodon +

-View the documentation at +The documentation currently uses Hugo to generate a static site from Markdown. Use `hugo serve` to test the site locally. + +View the live documentation at [https://docs.joinmastodon.org](https://docs.joinmastodon.org) diff --git a/archetypes/methods.md b/archetypes/methods.md index 261db256..ba3ea7f7 100644 --- a/archetypes/methods.md +++ b/archetypes/methods.md @@ -38,14 +38,14 @@ Authorization **Internal parameter.** Use HTTP `Link` header for pagination. -max_id -: String. Return results older than ID. +max_id +: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results. since_id -: String. Return results newer than ID. +: String. All results returned will be greater than this ID. In effect, sets a lower bound on results. min_id -: String. Return results immediately newer than ID. +: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward. limit : Integer. Maximum number of results to return. Defaults to 20 statuses or 40 accounts. Max twice the default limit. diff --git a/assets/manrope.scss b/assets/manrope.scss new file mode 100644 index 00000000..b36e069b --- /dev/null +++ b/assets/manrope.scss @@ -0,0 +1,8 @@ +@font-face { + font-family: "Manrope"; + font-style: normal; + font-weight: 100 900; + src: + url("/webfonts/manrope-variable.woff2") format("woff2 supports variations"), + url("/webfonts/manrope-variable.woff2") format("woff2-variations"); +} diff --git a/assets/style.scss b/assets/style.scss index 460744ac..b3437c61 100644 --- a/assets/style.scss +++ b/assets/style.scss @@ -1,17 +1,17 @@ @import 'fontawesome.scss'; -@import 'roboto.scss'; @import 'roboto-mono.scss'; +@import 'manrope.scss'; -$white: #fff ; // color5 +$white: #fff; // color5 $lightest: #d9e1e8; // color2 -$lighter: #9baec8; // color3 -$darkest: #1F232B; // color1 -$black: #000 ; // color8 -$darker: lighten($darkest, 34%); +$lighter: #9baec8; // color3 +$darkest: #1F232B; // color1 +$black: #000; // color8 +$darker: lighten($darkest, 34%); -$vibrant: #2b90d9; // color4 -$error: #df405a; // color6 -$success: #79bd9a; // color7 +$vibrant: #6364FF; // color4 +$error: #df405a; // color6 +$success: #79bd9a; // color7 $transition-in: 100ms linear; $transition-out: 250ms linear; @@ -24,19 +24,87 @@ $mobile-width: 600px; License: none (public domain) */ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { margin: 0; padding: 0; border: 0; @@ -45,8 +113,17 @@ time, mark, audio, video { } /* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { display: block; } @@ -55,19 +132,24 @@ body { box-sizing: border-box; text-rendering: optimizelegibility; font-feature-settings: "kern"; + -webkit-text-size-adjust: none; text-size-adjust: none; } -ol, ul { +ol, +ul { list-style: none; } -blockquote, q { +blockquote, +q { quotes: none; } -blockquote:before, blockquote:after, -q:before, q:after { +blockquote:before, +blockquote:after, +q:before, +q:after { content: ''; content: none; } @@ -83,7 +165,7 @@ a { body { box-sizing: border-box; - font-family: 'Roboto', sans-serif; + font-family: 'Manrope', sans-serif; text-rendering: optimizeLegibility; background-color: $darkest; color: $lighter; @@ -113,9 +195,51 @@ body { } .sidebar { - @media screen and (max-width: $mobile-width) { - margin-bottom: 60px; - } + @media screen and (max-width: $mobile-width) { + margin-bottom: 60px; + + // css menu toggle for mobile. + // when the checkbox is checked, the menu shows, + // othewise it's hidden. + // it's important for it all to be scoped inside this media query, + // that way on larger screens the menu will always show even if the checkbox is unchecked. + input[type="checkbox"] { + display: none; + } + label { + font-size: 1.2rem; + display: flex; + justify-content: flex-end; + margin-bottom: 2rem; + } + label i { + margin-right: .25rem; + } + input[type="checkbox"]:not(:checked) + label > .menu-open { + display: none; + } + + input[type="checkbox"]:checked + label > .menu-close { + display: none; + } + + & > ul { + display: none; + } + + #mobile-nav-toggle:checked ~ ul { + display: block; + } + } + + // on viewport sizes larger than mobile, always hide the mobile nav toggle. + @media screen and (min-width: $mobile-width) { + .mobile-nav-toggle, + .mobile-nav-toggle + label { + display: none; + } + } + .brand { display: flex; @@ -138,7 +262,7 @@ body { } } - & > ul > li { + &>ul>li { margin-bottom: 26px; &:last-child { @@ -153,7 +277,7 @@ body { text-transform: uppercase; } - & > ul a { + &>ul a { display: block; color: $white; text-decoration: none; @@ -290,6 +414,7 @@ main { line-height: 28px; font-weight: normal; margin-bottom: 26px; + -webkit-hyphens: auto; hyphens: auto; } @@ -345,13 +470,12 @@ main { width: 78px; margin-left: -78px; text-align: right; - padding-top: 4px; padding-right: 15px; content: '\2022'; } } - ol > li { + ol>li { &::before { padding-top: 0; content: counter(post) ". "; @@ -359,8 +483,8 @@ main { } } - li > ul, - li > ol { + li>ul, + li>ol { margin-top: 14px; } @@ -436,6 +560,7 @@ main { font-family: 'Roboto Mono', monospace; background-color: lighten($darkest, 8%); border-radius: 3px; + -webkit-hyphens: none; hyphens: none; white-space: pre; } @@ -451,7 +576,9 @@ main { line-height: 28px; font-weight: normal; margin-bottom: 26px; + -webkit-hyphens: auto; hyphens: auto; + dt { font-weight: 700; background: lighten($darkest, 8%); @@ -459,6 +586,7 @@ main { max-width: max-content; margin-bottom: 4px; } + dd { margin-left: 16px; margin-bottom: 16px; @@ -494,7 +622,7 @@ main { color: inherit; } - & > ul > li { + &>ul>li { margin-left: 0; &::before { @@ -533,7 +661,7 @@ main { margin-bottom: 20px; } - & > a { + &>a { font-weight: 500; color: $vibrant; background: #fff; @@ -737,7 +865,7 @@ main { width: 180px; font-size: 16px; - & > div:first-child { + &>div:first-child { margin-bottom: 2px; } } @@ -786,7 +914,7 @@ main { justify-content: center; flex-direction: row; - & > div { + &>div { display: flex; align-items: center; justify-content: center; @@ -822,7 +950,7 @@ main { color: $lightest; } - .logo-grid > div { + .logo-grid>div { flex-wrap: wrap; } @@ -840,4 +968,4 @@ main { align-content: center; margin-left: 0.25em; } -} \ No newline at end of file +} diff --git a/content/en/admin/backups.md b/content/en/admin/backups.md index 4d79def4..a40db098 100644 --- a/content/en/admin/backups.md +++ b/content/en/admin/backups.md @@ -20,21 +20,21 @@ Things that need to be backed up in order of importance: ## Failure modes {#failure} -There are two failure types that people in general may guard for: The failure of the hardware, such as data corruption on the disk; and human and software error, such as wrongful deletion of particular piece of data. In this documentation, only the former type is considered. +There are two failure types that people in general may guard for: The failure of the hardware, such as data corruption on the disk; and human and software error, such as wrongful deletion of a particular piece of data. In this documentation, only the former type is considered. -A lost PostgreSQL database is complete game over. Mastodon stores all the most important data in the PostgreSQL database. If the database disappears, all the accounts, posts and followers on your server will disappear with it. +Mastodon stores all the most important data in the PostgreSQL database. The loss of the PostgreSQL database will result in the complete failure of the server, including all the accounts, their posts and followers. -If you lose application secrets, some functions of Mastodon will stop working for your users, they will be logged out, two-factor authentication will become unavailable, Web Push API subscriptions will stop working. +If you lose application secrets, some functions of Mastodon will stop working for your users, they will be logged out, two-factor authentication will become unavailable, and Web Push API subscriptions will stop working. If you lose user-uploaded files, you will lose avatars, headers, and media attachments, but Mastodon _will_ work moving forward. -Losing the Redis database is almost harmless: The only irrecoverable data will be the contents of the Sidekiq queues and scheduled retries of previously failed jobs. The home and list feeds are stored in Redis, but can be regenerated with tootctl. +Losing the Redis database is almost harmless: The only irrecoverable data will be the contents of the Sidekiq queues and scheduled retries of previously failed jobs. The home and list feeds are stored in Redis but can be regenerated with tootctl. The best backups are so-called off-site backups, i.e. ones that are not stored on the same machine as Mastodon itself. If the server you are hosted on goes on fire and the hard disk drive explodes, backups stored on that same hard drive won’t be of much use. ## Backing up application secrets {#env} -Application secrets are the easiest to backup, since they never change. You only need to store `.env.production` somewhere safe. +Application secrets are the easiest to back up since they never change. You only need to store `.env.production` somewhere safe. ## Backing up PostgreSQL {#postgresql} diff --git a/content/en/admin/config.md b/content/en/admin/config.md index 2e48260d..9fde8c04 100644 --- a/content/en/admin/config.md +++ b/content/en/admin/config.md @@ -9,7 +9,7 @@ menu: Mastodon uses environment variables as its configuration. -For convenience, it can read them from a flat file called `.env.production` in the Mastodon directory (called a "dotenv" file), but they can always be overridden by a specific process. For example, systemd service files can read environment variables from an `EnvironmentFile` or from inline definitions with `Environment`, so you can have different configuration parameters for specific services. They can also be specified when calling Mastodon from the command line. +For convenience, it can read them from a flat file called `.env.production` in the Mastodon directory (called a "dotenv" file), but they can always be overridden by a specific process. For example, systemd service files can read environment variables from an `EnvironmentFile` or inline definitions with `Environment`, so you can have different configuration parameters for specific services. They can also be specified when calling Mastodon from the command line. ## Basic {#basic} @@ -21,11 +21,11 @@ This is the unique identifier of your server in the network. It cannot be safely #### `WEB_DOMAIN` -`WEB_DOMAIN` is an optional environment variable allowing to install Mastodon on one domain, while having the users' handles on a different domain, e.g. addressing users as `@alice@example.com` but accessing Mastodon on `mastodon.example.com`. This may be useful if your domain name is already used for a different website but you still want to use it as a Mastodon identifier because it looks better or shorter. +`WEB_DOMAIN` is an optional environment variable allowing the installation of Mastodon on one domain, while having the users' handles on a different domain, e.g. addressing users as `@alice@example.com` but accessing Mastodon on `mastodon.example.com`. This may be useful if your domain name is already used for a different website but you still want to use it as a Mastodon identifier because it looks better or shorter. -As with `LOCAL_DOMAIN`, `WEB_DOMAIN` cannot be safely changed once set, as this will confuse remote servers that knew of your previous settings and may break communication with them or make it unreliable. As the issues lie with remote servers' understanding of your accounts, re-installing Mastodon from scratch will not fix the issue. Therefore, please be extremely cautious when setting up `LOCAL_DOMAIN` and `WEB_DOMAIN`. +As with `LOCAL_DOMAIN`, `WEB_DOMAIN` cannot be safely changed once set, as this will confuse remote servers that know of your previous settings and may break communication with them or make it unreliable. As the issues lie with remote servers' understanding of your accounts, re-installing Mastodon from scratch will not fix the issue. Therefore, please be extremely cautious when setting up `LOCAL_DOMAIN` and `WEB_DOMAIN`. -To install Mastodon on `mastodon.example.com` in such a way it can serve `@alice@example.com`, set `LOCAL_DOMAIN` to `example.com` and `WEB_DOMAIN` to `mastodon.example.com`. This also requires additional configuration on the server hosting `example.com` to redirect or proxy requests to `https://example.com/.well-known/webfinger` to `https://mastodon.example.com/.well-known/webfinger`. For instance, with nginx, the configuration could look like the following: +To install Mastodon on `mastodon.example.com` in such a way it can serve `@alice@example.com`, set `LOCAL_DOMAIN` to `example.com` and `WEB_DOMAIN` to `mastodon.example.com`. This also requires additional configuration on the server hosting `example.com` to redirect requests from `https://example.com/.well-known/webfinger` to `https://mastodon.example.com/.well-known/webfinger`. For instance, with nginx, the configuration could look like the following: ``` location /.well-known/webfinger { @@ -40,7 +40,7 @@ If you have multiple domains pointed at your Mastodon server, this setting will #### `ALLOWED_PRIVATE_ADDRESSES` -Comma-separated specific addresses/subnets allowed in outgoing HTTP queries. +Comma-separated specific addresses/subnets are allowed in outgoing HTTP queries. #### `AUTHORIZED_FETCH` @@ -48,12 +48,12 @@ Also called "secure mode". When set to `true`, the following changes occur: - Mastodon will stop generating linked-data signatures for public posts, which prevents them from being re-distributed efficiently but without precise control. Since a linked-data object with a signature is entirely self-contained, it can be passed around without making extra requests to the server where it originates. - Mastodon will require HTTP signature authentication on ActivityPub representations of public posts and profiles, which are normally available without any authentication. Profiles will only return barebones technical information when no authentication is supplied. -- Prior to v4.0.0: Mastodon will require any REST/streaming API access to have a user context (i.e. having gone through an OAuth authorization screen with an active user), when normally some API endpoints are available without any authentication. +- Prior to v4.0.0: Mastodon will require any REST/streaming API access to have a user context (i.e. having gone through an OAuth authorization screen with an active user) when normally some API endpoints are available without any authentication. As a result, through the authentication mechanism and avoiding re-distribution mechanisms that do not have your server in the loop, it becomes possible to enforce who can and cannot retrieve even public content from your server, e.g. servers whose domains you have blocked. {{< hint style="warning" >}} -Unfortunately, secure mode is not without its drawbacks, which is why it is not enabled by default. Not all software in the fediverse can support it fully, in particular some functionality will be broken with Mastodon servers older than 3.0; you lose some useful functionality even with up-to-date servers since linked-data signatures are used to make public conversation threads more complete; and because an authentication mechanism on public content means no caching is possible, it comes with an increased computational cost. +Unfortunately, secure mode is not without its drawbacks, which is why it is not enabled by default. Not all software in the fediverse can support it fully, in particular, some functionality will be broken with Mastodon servers older than 3.0; you lose some useful functionality even with up-to-date servers since linked-data signatures are used to make public conversation threads more complete; and because an authentication mechanism on public content means no caching is possible, it comes with an increased computational cost. {{}} {{< hint style="warning" >}} @@ -80,15 +80,15 @@ This setting was known as `WHITELIST_MODE` prior to 3.1.5. #### `DISALLOW_UNAUTHENTICATED_API_ACCESS` -As of Mastodon v4.0.0, the web app is now used to render all requests, even for logged-out viewers. In order to make these views work, the web app makes public API requests in order to fetch accounts and statuses. If you would like to disallow this, then set this variable to `true`. Note that disallowing unauthenticated API access will cause profile and post permalinks to return an error to logged-out users, essentially making it so that the only ways to view content is to either log in locally or fetch it via ActivityPub. +As of Mastodon v4.0.0, the web app is now used to render all requests, even for logged-out viewers. To make these views work, the web app makes public API requests to fetch accounts and statuses. If you would like to disallow this, then set this variable to `true`. Note that disallowing unauthenticated API access will cause profile and post permalinks to return an error to logged-out users, essentially making it so that the only way to view content is to either log in locally or fetch it via ActivityPub. #### `SINGLE_USER_MODE` -If set to `true`, the frontpage of your Mastodon server will always redirect to the first profile in the database and registrations will be disabled. +If set to `true`, the front page of your Mastodon server will always redirect to the first profile in the database and registrations will be disabled. #### `DEFAULT_LOCALE` -By default, Mastodon will automatically detect the visitor's language from browser headers and display the Mastodon interface in that language (if it's supported). If you are running a language-specific or regional server, that behaviour may mislead visitors who do not speak your language into signing up on your server. For this reason, you may want to set this variable to a specific language. +By default, Mastodon will automatically detect the visitor's language from browser headers and display the Mastodon interface in that language (if it's supported). If you are running a language-specific or regional server, that behavior may mislead visitors who do not speak your language into signing up on your server. For this reason, you may want to set this variable to a specific language. Example value: `de` @@ -198,19 +198,24 @@ If set to true, Mastodon will answer requests for files in its `public` director #### `RAILS_LOG_LEVEL` -Determines the amount of logs generated by Mastodon. Defaults to `info`, which generates a log entry about every request served by Mastodon and every background job processed by Mastodon. This can be useful but can get quite noisy and strain the I/O of your machine if there is a lot of traffic/activity. In that case, `warn` is recommended, which will only output information about things that are going wrong, and otherwise stay quiet. Possible values are `debug`, `info`, `warn`, `error`, `fatal` and `unknown`. +Determines the amount of logs generated by Mastodon for the web and Sidekiq processes. Defaults to `info`, which generates a log entry about every request served by Mastodon and every background job processed by Mastodon. This can be useful but can get quite noisy and strain the I/O of your machine if there is a lot of traffic/activity. In that case, `warn` is recommended, which will only output information about things that are going wrong, and otherwise stay quiet. Possible values are `debug`, `info`, `warn`, `error`, `fatal` and `unknown`. + +#### `LOG_LEVEL` + +Determines the amount of logs generated by Mastodon for the streaming processes. Defaults to `info`. Possible values are `silly` and `info`. #### `TRUSTED_PROXY_IP` Tells the Mastodon web and streaming processes which IPs act as your trusted reverse proxy (e.g. nginx, Cloudflare). It affects how Mastodon determines the source IP of each request, which is used for important rate limits and security functions. If the value is set incorrectly then Mastodon could use the IP of the reverse proxy instead of the actual source. -By default the loopback and private network address ranges are trusted. Specifically: - * `127.0.0.1/8` - * `::1/128` - * `10.0.0.0/8` - * `172.16.0.0/12` - * `192.168.0.0/16` - * `fc00::/7` +By default, the loopback and private network address ranges are trusted. Specifically: + +- `127.0.0.1/8` +- `::1/128` +- `10.0.0.0/8` +- `172.16.0.0/12` +- `192.168.0.0/16` +- `fc00::/7` If you're using a single reverse proxy and it runs on the same machine or is in the same private network as your Mastodon web and streaming processes then you most likely don't need to modify this setting and can use the default. Or if you're using multiple reverse proxy servers and they're all in the same private network as your Mastodon web and streaming processes then, again, the default should be fine. However, if you're using a reverse proxy server that reaches your Mastodon web and streaming servers via a public IP address (for example if you're using Cloudflare or a similar proxy) then you'll need to set this variable. It should be the IPs of all reverse proxies in use, as a comma-separated list of IPs or IP ranges using [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation). Note that when this variable is set the default ranges (mentioned above) will no longer be trusted, so if you have both an external reverse proxy _and_ a proxy on localhost then you must include the IPs (or IP ranges) of both. @@ -276,7 +281,11 @@ The streaming API can be deployed to a different domain/subdomain. This may impr Example value: `wss://streaming.example.com` -#### `STREAMING_CLUSTER_NUM` +#### `STREAMING_CLUSTER_NUM` (deprecated) {#streaming_cluster_num} + +{{< hint style="danger" >}} +Deprecated: The streaming server process now only uses a single node.js process, to scale it further, you'll need to follow the documentation in the [scaling guide](/admin/scaling#streaming) +{{< /hint >}} Specific to the streaming API, this variable determines how many different processes the streaming API forks into. Defaults to the number of CPU cores minus one. @@ -306,11 +315,11 @@ Defaults to `5432`. #### `DB_POOL` -How many database connections to pool in the process. This value should cover every thread in the process, for this reason, it defaults to the value of `MAX_THREADS`. +Defines how many database connections to pool in the process. This value should cover every thread in the process, for this reason, it defaults to the value of `MAX_THREADS`. #### `DB_SSLMODE` -Postgres's [SSL mode](https://www.postgresql.org/docs/10/libpq-ssl.html). Defaults to `prefer`. +PostgreSQL [SSL mode](https://www.postgresql.org/docs/10/libpq-ssl.html). Defaults to `prefer`. #### `DATABASE_URL` @@ -340,53 +349,65 @@ Example value: `redis://user:password@localhost:6379` #### `REDIS_NAMESPACE` -If provided, namespaces all Redis keys. This allows sharing the same Redis database between different projects or Mastodon servers. +If provided, namespaces all Redis keys. This allows the sharing of the same Redis database between different projects or Mastodon servers. #### `CACHE_REDIS_HOST` -Defaults to value of `REDIS_HOST`. +Defaults to the value of `REDIS_HOST`. #### `CACHE_REDIS_PORT` -Defaults to value of `REDIS_PORT`. +Defaults to the value of `REDIS_PORT`. #### `CACHE_REDIS_URL` -If provided, takes precedence over `CACHE_REDIS_HOST` and `CACHE_REDIS_PORT`. Defaults to value of `REDIS_URL`. +If provided, takes precedence over `CACHE_REDIS_HOST` and `CACHE_REDIS_PORT`. Defaults to the value of `REDIS_URL`. #### `CACHE_REDIS_NAMESPACE` -Defaults to value of `REDIS_NAMESPACE`. +Defaults to the value of `REDIS_NAMESPACE`. #### `SIDEKIQ_REDIS_URL` -### ElasticSearch {#elasticsearch} +### Elasticsearch {#elasticsearch} -{{< page-ref page="admin/optional/elasticsearch" >}} +{{< page-ref page="admin/elasticsearch" >}} #### `ES_ENABLED` -If set to `true`, Mastodon will use ElasticSearch for its search functions. +If set to `true`, Mastodon will use Elasticsearch for its search functions. + +#### `ES_PRESET` + +It controls the Elasticsearch indices configuration (number of shards and replica). + +Possible values are: + +- `single_node_cluster` (default) +- `small_cluster` +- `large_cluster` + +See the [Elasticsearch setup page for details on each setting](../elasticsearch#choosing-the-correct-preset). #### `ES_HOST` -Host of the ElasticSearch server. Defaults to `localhost` +Host of the Elasticsearch server. Defaults to `localhost`. If using TLS, prepend the hostname with `https://`. For example: `https://elastic.example.com`. #### `ES_PORT` -Port of the ElasticSearch server. Defaults to `9200` +Port of the Elasticsearch server. Defaults to `9200` #### `ES_USER` -Used for optionally authenticating with ElasticSearch +Used for optionally authenticating with Elasticsearch #### `ES_PASS` -Used for optionally authenticating with ElasticSearch +Used for optionally authenticating with Elasticsearch #### `ES_PREFIX` -Useful if the ElasticSearch server is shared between multiple projects or different Mastodon servers. Defaults to value of `REDIS_NAMESPACE`. +Useful if the Elasticsearch server is shared between multiple projects or different Mastodon servers. Defaults to the value of `REDIS_NAMESPACE`. ### StatsD {#statsd} @@ -400,19 +421,34 @@ Example value: `localhost:8125` If set, all StatsD keys will be prefixed with this. Defaults to `Mastodon.production` when `RAILS_ENV` is `production`, `Mastodon.development` when it's `development`, etc. +#### `STATSD_SIDEKIQ` + +If set to `true`, Mastodon will log some Sidekiq metrics into StatsD. Defaults to `false`. + ### SMTP email delivery {#smtp} #### `SMTP_SERVER` + #### `SMTP_PORT` + #### `SMTP_LOGIN` + #### `SMTP_PASSWORD` + #### `SMTP_FROM_ADDRESS` + #### `SMTP_DOMAIN` + #### `SMTP_DELIVERY_METHOD` + #### `SMTP_AUTH_METHOD` + #### `SMTP_CA_FILE` + #### `SMTP_OPENSSL_VERIFY_MODE` + #### `SMTP_ENABLE_STARTTLS_AUTO` + #### `SMTP_ENABLE_STARTTLS` Set to `auto` (default), `always`, or `never`. @@ -421,11 +457,39 @@ Set to `auto` (default), `always`, or `never`. 4.0.0 - added #### `SMTP_TLS` + #### `SMTP_SSL` +E-mail configuration is based on the *action_mailer* component of the *Ruby on Rails* framework that Mastodon is built on. Complete documentation on action_mailer is available [here](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration). The client uses SMTP or derivatives: StartTLS + SMTP or SMTPS (SMTP over TLS). + +### Basic configuration {#basic} + +* `SMTP_SERVER`: Specify the server to use. For example `sub.domain.tld`. +* `SMTP_PORT`: By default, the value is `25` (the usual port for SMTP). If StartTLS is detected, it may be switched to port 587. +* `SMTP_DOMAIN`: Only required if a HELO domain is needed. Will be set to the `SMTP_SERVER` domain by default. +* `SMTP_FROM_ADDRESS`: Specify a sender address. +* `SMTP_DELIVERY_METHOD`: By default, the value is `smtp` (can also be `sendmail`). + +### Authentication for the SMTP server {#smtpauthentication} + +* `SMTP_LOGIN`: Login for the SMTP user. +* `SMTP_PASSWORD`: Password for the SMTP user. +* `SMTP_AUTH_METHOD`: Either `plain` (default; the password is transmitted in the clear), `login` (password will be base64 encoded) or `cram_md5`. + +### Secured SMTP +By default, a StartTLS connection will be attempted to the specified SMTP server. + +* `SMTP_ENABLE_STARTTLS_AUTO`: Default `true`. +* `SMTP_CA_FILE`: A value may be specified, but on many Linux distros (e.g. Debian-based) this will be `/etc/ssl/certs/ca-certificates.crt`. +* `SMTP_OPENSSL_VERIFY_MODE`: `none` or `peer`. When using TLS, it may be useful to accept connections with a self-signed certificate. +* `SMTP_TLS`: `true` or `false` (default `false`) +* `SMTP_SSL`: `true` or `false` (default `false`) + +Note that `TLSv1.3` and `TLSv1.2` are the only SSL/TLS protocols currently considered to be secure. + ## File storage {#files} -### CDN {cdn} +### CDN {#cdn} #### `CDN_HOST` @@ -439,9 +503,9 @@ You must serve the files with CORS headers, otherwise some functions of Mastodon #### `S3_ALIAS_HOST` -Similar to `CDN_HOST`, you may serve *user-uploaded* files from a separate host. In fact, if you are using external storage like Amazon S3, Minio or Google Cloud, you will by default be serving files from those services' URLs. +Similar to `CDN_HOST`, you may serve _user-uploaded_ files from a separate host. If you are using external storage like Amazon S3, Minio or Google Cloud, you will by default be serving files from those services' URLs. -It is *extremely recommended* to use your own host instead, for a few reasons: +It is _extremely recommended_ to use your own host instead, for a few reasons: 1. Bandwidth on external storage providers is metered and expensive 2. You may want to switch to a different provider later without breaking old links @@ -457,36 +521,67 @@ You must serve the files with CORS headers, otherwise some functions of Mastodon ### Local file storage {#paperclip} #### `PAPERCLIP_ROOT_PATH` + #### `PAPERCLIP_ROOT_URL` ### Amazon S3 and compatible {#s3} #### `S3_ENABLED` + #### `S3_BUCKET` + #### `AWS_ACCESS_KEY_ID` + #### `AWS_SECRET_ACCESS_KEY` + #### `S3_REGION` + #### `S3_PROTOCOL` + #### `S3_HOSTNAME` + #### `S3_ENDPOINT` + #### `S3_SIGNATURE_VERSION` + #### `S3_OVERRIDE_PATH_STYLE` + #### `S3_OPEN_TIMEOUT` + #### `S3_READ_TIMEOUT` + #### `S3_FORCE_SINGLE_REQUEST` +#### `S3_BATCH_DELETE_LIMIT` + +The official [Amazon S3 API](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) can handle deleting 1,000 objects in one batch job, but some providers may have issues handling this many in one request, or offer lower limits. Defaults to `1000`. + +#### `S3_BATCH_DELETE_RETRY` + +During batch delete operations, S3 providers may perodically fail or timeout while processing deletion requests. Mastodon will backoff and retry the request up to the maximum number of times. Defaults to `3`. + ### Swift {#swift} #### `SWIFT_ENABLED` + #### `SWIFT_USERNAME` + #### `SWIFT_TENANT` + #### `SWIFT_PASSWORD` + #### `SWIFT_PROJECT_ID` + #### `SWIFT_AUTH_URL` + #### `SWIFT_CONTAINER` + #### `SWIFT_OBJECT_URL` + #### `SWIFT_REGION` + #### `SWIFT_DOMAIN_NAME` + #### `SWIFT_CACHE_TTL` ## External authentication {#external-authentication} @@ -495,74 +590,137 @@ You must serve the files with CORS headers, otherwise some functions of Mastodon #### `OMNIAUTH_ONLY` +#### `ONE_CLICK_SSO_LOGIN` +Enables the `Login or Register` button. +Useful for instances where all authentication takes place using a single +external provider (CAS, SAML or OIDC). + +Enabling this will prevent caching for anonymous sessions. +And, when using OIDC discovery, the identity provider has to be available +before Mastodon starts. + ### LDAP {#ldap} #### `LDAP_ENABLED` + #### `LDAP_HOST` + #### `LDAP_PORT` + #### `LDAP_METHOD` + #### `LDAP_BASE` + #### `LDAP_BIND_DN` + #### `LDAP_PASSWORD` + #### `LDAP_UID` + #### `LDAP_SEARCH_FILTER` + #### `LDAP_MAIL` -#### `LDAP_UID_CONVERSTION_ENABLED` + +#### `LDAP_UID_CONVERSION_ENABLED` ### PAM {#pam} #### `PAM_ENABLED` + #### `PAM_EMAIL_DOMAIN` + #### `PAM_DEFAULT_SERVICE` + #### `PAM_CONTROLLED_SERVICE` ### CAS {#cas} #### `CAS_ENABLED` + #### `CAS_DISPLAY_NAME` + #### `CAS_URL` + #### `CAS_HOST` + #### `CAS_PORT` + #### `CAS_SSL` + #### `CAS_VALIDATE_URL` + #### `CAS_CALLBACK_URL` + #### `CAS_LOGOUT_URL` + #### `CAS_LOGIN_URL` + #### `CAS_UID_FIELD` + #### `CAS_CA_PATH` + #### `CAS_DISABLE_SSL_VERIFICATION` + #### `CAS_UID_KEY` + #### `CAS_NAME_KEY` + #### `CAS_EMAIL_KEY` + #### `CAS_NICKNAME_KEY` + #### `CAS_FIRST_NAME_KEY` + #### `CAS_LAST_NAME_KEY` + #### `CAS_LOCATION_KEY` + #### `CAS_IMAGE_KEY` + #### `CAS_PHONE_KEY` + #### `CAS_SECURITY_ASSUME_EMAIL_IS_VERIFIED` ### SAML {#saml} #### `SAML_ENABLED` + #### `SAML_ACS_URL` + #### `SAML_ISSUER` + #### `SAML_IDP_SSO_TARGET_URL` + #### `SAML_IDP_CERT` + #### `SAML_IDP_CERT_FINGERPRINT` + #### `SAML_NAME_IDENTIFIER_FORMAT` + #### `SAML_CERT` + #### `SAML_PRIVATE_KEY` + #### `SAML_SECURITY_WANT_ASSERTION_SIGNED` + #### `SAML_SECURITY_WANT_ASSERTION_ENCRYPTED` + #### `SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED` + #### `SAML_ATTRIBUTES_STATEMENTS_UID` + #### `SAML_ATTRIBUTES_STATEMENTS_EMAIL` + #### `SAML_ATTRIBUTES_STATEMENTS_FULL_NAME` + #### `SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME` + #### `SAML_ATTRIBUTES_STATEMENTS_LAST_NAME` + #### `SAML_UID_ATTRIBUTE` + #### `SAML_ATTRIBUTES_STATEMENTS_VERIFIED` + #### `SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL` ## Hidden services {#hidden-services} @@ -572,7 +730,9 @@ You must serve the files with CORS headers, otherwise some functions of Mastodon {{< page-ref page="admin/optional/tor" >}} #### `http_proxy` + #### `http_hidden_proxy` + #### `ALLOW_ACCESS_TO_HIDDEN_SERVICE` ## Limits {#limits} @@ -588,14 +748,14 @@ If set, registrations will not be possible with any e-mails **except** those fro If set, registrations will not be possible with any e-mails from the specified domains. Pipe-separated values, e.g.: `foo.com|bar.com` {{< hint style="warning" >}} -This option is deprecated. You can dynamically block e-mail domains from the admin interface or from the `tootctl` command-line interface. +This option is deprecated. You can dynamically block e-mail domains from the admin interface or the `tootctl` command-line interface. {{}} ### Sessions #### `MAX_SESSION_ACTIVATIONS` -How many browser sessions are allowed per-user. Defaults to `10`. If a new browser session is created, then the oldest session is deleted, e.g. user in that browser is logged out. +Defines the maximum number of browser sessions allowed per user, which defaults to 10. If a new browser session is created and the limit is exceeded, the oldest session is deleted, resulting in the user being logged out of that session. ### Home feeds @@ -620,13 +780,21 @@ This variable only has any effect when running `rake db:migrate` and it is extre ### Uncategorized or unsorted #### `BUNDLE_GEMFILE` + #### `DEEPL_API_KEY` + #### `DEEPL_PLAN` + #### `LIBRE_TRANSLATE_ENDPOINT` + #### `LIBRE_TRANSLATE_API_KEY` + #### `CACHE_BUSTER_ENABLED` + #### `CACHE_BUSTER_SECRET_HEADER` + #### `CACHE_BUSTER_SECRET` + #### `GITHUB_REPOSITORY` Defaults to `mastodon/mastodon` @@ -636,8 +804,11 @@ Defaults to `mastodon/mastodon` Defaults to `https://github.com/$GITHUB_REPOSITORY` #### `FFMPEG_BINARY` + #### `LOCAL_HTTPS` + #### `PATH` + #### `MAX_FOLLOWS_THRESHOLD` Defaults to `7500` @@ -670,4 +841,5 @@ Defaults to `512`. #### `GITHUB_API_TOKEN` -Used in a rake task for generating AUTHORS.md from Github commit history. +Used in a rake task for generating AUTHORS.md from GitHub commit history. + diff --git a/content/en/admin/elasticsearch.md b/content/en/admin/elasticsearch.md new file mode 100644 index 00000000..17bb3e2b --- /dev/null +++ b/content/en/admin/elasticsearch.md @@ -0,0 +1,231 @@ +--- +title: Configuring full-text search +description: Setting up Elasticsearch to search for statuses (authored, favourited, or mentioned), public indexable status, and accounts +aliases: +- /admin/optional/elasticsearch +menu: + docs: + weight: 40 + parent: admin +--- + +Mastodon supports full-text search when Elasticsearch is available. It is strongly recommended to configure this feature. + +Mastodon’s full-text search allows logged-in users to find results from: +- public statuses from accounts that opted into appearing in search results +- their own statuses +- their mentions +- their favourites +- their bookmarks +- accounts (display name, usernames and bios) + +It deliberately does not allow searching for arbitrary strings in the entire database. + +## Installing Elasticsearch {#install} + +{{< hint style="info" >}} +Mastodon is tested with Elasticsearch version 7. It should support OpenSearch, as well as Elasticsearch versions 6 and 8, but those setups are not officially supported. +{{< /hint >}} + +Elasticsearch requires a Java runtime. If you don’t have Java already installed, do it now. Assuming you are logged in as `root`: + +```bash +apt install openjdk-17-jre-headless +``` + +Add the official Elasticsearch repository to apt: + +```bash +wget -O /usr/share/keyrings/elasticsearch.asc https://artifacts.elastic.co/GPG-KEY-elasticsearch +echo "deb [signed-by=/usr/share/keyrings/elasticsearch.asc] https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elastic-7.x.list +``` + +Now you can install Elasticsearch: + +```bash +apt update +apt install elasticsearch +``` + +{{< hint style="warning" >}} +**Security warning:** By default, Elasticsearch is supposed to bind to localhost only, i.e. be inaccessible from the outside network. You can check which address Elasticsearch binds to by looking at `network.host` within `/etc/elasticsearch/elasticsearch.yml`. Consider that anyone who can access Elasticsearch can access and modify any data within it, as there is no authentication layer. So it’s really important that the access is secured. Having a firewall that only exposes the 22, 80 and 443 ports is advisable, as outlined in the [main installation instructions](../../prerequisites/#install-a-firewall-and-only-whitelist-ssh-http-and-https-ports). If you have a multi-host setup, you must know how to secure internal traffic. +{{< /hint >}} + +To start Elasticsearch: + +```bash +systemctl daemon-reload +systemctl enable --now elasticsearch +``` + +## Configuring Mastodon {#config} + +Edit `.env.production` to add the following variables: + +```bash +ES_ENABLED=true +ES_HOST=localhost +ES_PORT=9200 +ES_PRESET= # single_node_cluster, small_cluster or large_cluster +# ES_USER= +# ES_PASS= +``` + +_Note_: If using TLS, prepend the hostname with `https://`. For example: `https://elastic.example.com`. + +### Choosing the correct preset + +The value for `ES_PRESET` depends on the size of your Elasticsearch and will be used to set the number of shards and replicas for your indices to the best value for your setup: +- `single_node_cluster` if you only have one node in your Elasticsearch cluster. Indices will be configured without any replica +- `small_cluster` if you have less than 6 nodes in your cluster. Indices will be configured with 1 replica +- `large_cluster` if you have 6 or more nodes in your cluster. Indices will be configured with more shards than with the `small_cluster` setting, to allow them to be distributed over more nodes + +If you have multiple Mastodon servers on the same machine, and you are planning to use the same Elasticsearch installation for all of them, make sure that all of them have unique `REDIS_NAMESPACE` in their configurations, to differentiate the indices. If you need to override the prefix of the Elasticsearch indices, you can set `ES_PREFIX` directly. + +### Security + +By default, Elasticsearch does not handle any authentication and every request is made with full admin permission. We strongly advise you to configure Elasticsearch security features on your cluster. + +To configure it, please refer [to the official documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/security-minimal-setup.html). It will guide you through: +- Enabling the security features (`xpack.security.enabled: true`) +- Creating passwords for built-in users + +Once done, you can create a custom role for Mastodon to connect. + +For example (please adapt this snippet to use your Elastic admin password): + +```sh +curl -X POST -u elastic:admin_password "localhost:9200/_security/role/mastodon_full_access?pretty" -H 'Content-Type: application/json' -d' +{ + "cluster": ["monitor"], + "indices": [{ + "names": ["*"], + "privileges": ["read", "monitor", "write", "manage"] + }] +} +' +``` + +[Elasticsearch documentation for role creation](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/security-api-put-role.html) + +Once the role is created, you can create a user for the Mastodon server to use, and assign it the role. + +For example (please adapt this snippet to use your Elastic admin password, and customize your new user `mastodon` user password): + +```sh +curl -X POST -u elastic:admin_password "localhost:9200/_security/user/mastodon?pretty" -H 'Content-Type: application/json' -d' +{ + "password" : "l0ng-r4nd0m-p@ssw0rd", + "roles" : ["mastodon_full_access"] +} +' +``` + +[Elasticsearch documentation for user creation](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/security-api-put-user.html) + +Once this is done, you need to configure Mastodon to use the credentials for your newly created user. + +In `.env.production`, adjust your configuration: + +```bash +ES_USER=mastodon +ES_PASS=l0ng-r4nd0m-p@ssw0rd +``` + +You are all set, and your Elasticsearch server should be much more secure! + +### Populate the indices + +After saving the new configuration, restart Mastodon processes for it to take effect: + +```bash +systemctl restart mastodon-sidekiq +systemctl reload mastodon-web +``` + +Now it's time to create the Elasticsearch indices and fill them with data: + +```bash +su - mastodon +cd live +RAILS_ENV=production bin/tootctl search deploy +``` + +## Search optimization for other languages +### Chinese search optimization {#chinese-search-optimization} + +The standard analyzer is the default for Elasticsearch, but for some languages like Chinese it may not be the optimal choice. To enhance the search experience, consider installing a language-specific analyzer. Before creating indices in Elasticsearch, be sure to install the following extensions: + +- [elasticsearch-analysis-ik](https://github.com/medcl/elasticsearch-analysis-ik) +- [elasticsearch-analysis-stconvert](https://github.com/medcl/elasticsearch-analysis-stconvert) + +And then modify Mastodon's index definition as follows: + +```diff +diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb +--- a/app/chewy/accounts_index.rb ++++ b/app/chewy/accounts_index.rb +@@ -4,7 +4,7 @@ class AccountsIndex < Chewy::Index + settings index: { refresh_interval: '5m' }, analysis: { + analyzer: { + content: { +- tokenizer: 'whitespace', ++ tokenizer: 'ik_max_word', + filter: %w(lowercase asciifolding cjk_width), + }, + +diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb +--- a/app/chewy/statuses_index.rb ++++ b/app/chewy/statuses_index.rb +@@ -16,9 +16,17 @@ class StatusesIndex < Chewy::Index + language: 'possessive_english', + }, + }, ++ char_filter: { ++ tsconvert: { ++ type: 'stconvert', ++ keep_both: false, ++ delimiter: '#', ++ convert_type: 't2s', ++ }, ++ }, + analyzer: { + content: { +- tokenizer: 'uax_url_email', ++ tokenizer: 'ik_max_word', + filter: %w( + english_possessive_stemmer + lowercase +@@ -27,6 +35,7 @@ class StatusesIndex < Chewy::Index + english_stop + english_stemmer + ), ++ char_filter: %w(tsconvert), + }, + }, + } +diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb +--- a/app/chewy/tags_index.rb ++++ b/app/chewy/tags_index.rb +@@ -2,10 +2,19 @@ + + class TagsIndex < Chewy::Index + settings index: { refresh_interval: '15m' }, analysis: { ++ char_filter: { ++ tsconvert: { ++ type: 'stconvert', ++ keep_both: false, ++ delimiter: '#', ++ convert_type: 't2s', ++ }, ++ }, + analyzer: { + content: { +- tokenizer: 'keyword', ++ tokenizer: 'ik_max_word', + filter: %w(lowercase asciifolding cjk_width), ++ char_filter: %w(tsconvert), + }, + + edge_ngram: { +``` diff --git a/content/en/admin/install.md b/content/en/admin/install.md index 2105f536..01ebedf6 100644 --- a/content/en/admin/install.md +++ b/content/en/admin/install.md @@ -17,7 +17,7 @@ You will be running the commands as root. If you aren’t already root, switch t ### System repositories {#system-repositories} -Make sure curl, wget, gnupg, apt-transport-https, lsb-release and ca-certificates is installed first: +Make sure curl, wget, gnupg, apt-transport-https, lsb-release and ca-certificates are installed first: ```bash apt install -y curl wget gnupg apt-transport-https lsb-release ca-certificates @@ -58,7 +58,7 @@ yarn set version classic ### Installing Ruby {#installing-ruby} -We will be using rbenv to manage Ruby versions, because it’s easier to get the right versions and to update once a newer release comes out. rbenv must be installed for a single Linux user, therefore, first we must create the user Mastodon will be running as: +We will use rbenv to manage Ruby versions as it simplifies obtaining the correct versions and updating them when new releases are available. Since rbenv needs to be installed for an individual Linux user, we must first create the user account under which Mastodon will run: ```bash adduser --disabled-login mastodon @@ -74,7 +74,6 @@ And proceed to install rbenv and rbenv-build: ```bash git clone https://github.com/rbenv/rbenv.git ~/.rbenv -cd ~/.rbenv && src/configure && make -C src echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc exec bash @@ -84,11 +83,11 @@ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build Once this is done, we can install the correct Ruby version: ```bash -RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.0.4 -rbenv global 3.0.4 +RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.2.2 +rbenv global 3.2.2 ``` -We’ll also need to install bundler: +We’ll also need to install the bundler: ```bash gem install bundler --no-document @@ -106,7 +105,7 @@ exit #### Performance configuration (optional) {#performance-configuration-optional} -For optimal performance, you may use [pgTune](https://pgtune.leopard.in.ua/#/) to generate an appropriate configuration and edit values in `/etc/postgresql/15/main/postgresql.conf` before restarting PostgreSQL with `systemctl restart postgresql` +For optimal performance, you may use [pgTune](https://pgtune.leopard.in.ua/#/) to generate an appropriate configuration and edit values in `/etc/postgresql/16/main/postgresql.conf` before restarting PostgreSQL with `systemctl restart postgresql` #### Creating a user {#creating-a-user} @@ -141,7 +140,7 @@ Use git to download the latest stable release of Mastodon: ```bash git clone https://github.com/mastodon/mastodon.git live && cd live -git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1) +git checkout $(git tag -l | grep '^v[0-9.]*$' | sort -V | tail -n 1) ``` #### Installing the last dependencies {#installing-the-last-dependencies} @@ -181,6 +180,16 @@ You’re done with the mastodon user for now, so switch back to root: exit ``` +### Acquiring an SSL certificate {#acquiring-a-ssl-certificate} + +We’ll use Let’s Encrypt to get a free SSL certificate: + +```bash +certbot certonly --nginx -d example.com +``` + +This will obtain the certificate, and save it in the directory `/etc/letsencrypt/live/example.com/`. + ### Setting up nginx {#setting-up-nginx} Copy the configuration template for nginx from the Mastodon directory: @@ -188,28 +197,30 @@ Copy the configuration template for nginx from the Mastodon directory: ```bash cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon +rm /etc/nginx/sites-enabled/default ``` -Then edit `/etc/nginx/sites-available/mastodon` to replace `example.com` with your own domain name, and make any other adjustments you might need. +Then edit `/etc/nginx/sites-available/mastodon` to + +1. Replace `example.com` with your own domain name +2. Uncomment the `ssl_certificate` and `ssl_certificate_key` lines and replace the two lines with (ignore this step if you are bringing your own certificate) + +``` +ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; +ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; +``` + +3. Make any other adjustments you might need. + +Un-comment the lines starting with `ssl_certificate` and `ssl_certificate_key`, updating the path with the correct domain name. Reload nginx for the changes to take effect: - ```bash systemctl reload nginx ``` -### Acquiring a SSL certificate {#acquiring-a-ssl-certificate} - -We’ll use Let’s Encrypt to get a free SSL certificate: - -```bash -certbot --nginx -d example.com -``` - -This will obtain the certificate, automatically update `/etc/nginx/sites-available/mastodon` to use the new certificate, and reload nginx for the changes to take effect. - -At this point you should be able to visit your domain in the browser and see the elephant hitting the computer screen error page. This is because we haven’t started the Mastodon process yet. +At this point, you should be able to visit your domain in the browser and see the elephant hitting the computer screen error page. This is because we haven’t started the Mastodon process yet. ### Setting up systemd services {#setting-up-systemd-services} diff --git a/content/en/admin/migrating.md b/content/en/admin/migrating.md index a92a5a55..0e1c4091 100644 --- a/content/en/admin/migrating.md +++ b/content/en/admin/migrating.md @@ -7,7 +7,7 @@ menu: parent: admin --- -Sometimes, for various reasons, you may want to migrate your Mastodon instance from one server to another. Fortunately this is not too difficult of a process, although it may result in some downtime. +Sometimes, for various reasons, you may want to migrate your Mastodon instance from one server to another. Fortunately, this is not too difficult of a process, although it may result in some downtime. {{< hint style="info" >}} This guide was written with Ubuntu Server in mind; your mileage may vary for other setups. @@ -17,14 +17,14 @@ This guide was written with Ubuntu Server in mind; your mileage may vary for oth 1. Set up a new Mastodon server using the [Production Guide]({{< relref "install" >}}) (however, don’t run `mastodon:setup`). 2. Stop Mastodon on the old server (e.g. `systemctl stop 'mastodon-*.service'`). -3. Dump and load the Postgres database using the instructions below. +3. Dump and load the PostgreSQL database using the instructions below. 4. Copy the `system/` files using the instructions below. (Note: if you’re using S3, you can skip this step.) 5. Copy the `.env.production` file. 6. Run `RAILS_ENV=production bundle exec rails assets:precompile` to compile Mastodon 7. Run `RAILS_ENV=production ./bin/tootctl feeds build` to rebuild the home timelines for each user. 8. Start Mastodon on the new server. 9. Update your DNS settings to point to the new server. -10. Update or copy your Nginx configuration, re-run LetsEncrypt as necessary. +10. Update or copy your Nginx configuration, and re-run LetsEncrypt as necessary. 11. Enjoy your new server! ## Detailed steps {#detailed-steps} @@ -34,18 +34,18 @@ This guide was written with Ubuntu Server in mind; your mileage may vary for oth At a high level, you’ll need to copy over the following: * The `~/live/public/system` directory, which contains user-uploaded images and videos (if using S3, you don’t need this) -* The Postgres database (using [pg_dump](https://www.postgresql.org/docs/9.1/static/backup-dump.html)) +* The PostgreSQL database (using [pg_dump](https://www.postgresql.org/docs/9.1/static/backup-dump.html)) * The `~/live/.env.production` file, which contains server config and secrets Less crucially, you’ll probably also want to copy the following for convenience: -* The nginx config (under `/etc/nginx/sites-available/default`) +* The nginx config (under `/etc/nginx/sites-available/mastodon`) * The systemd config files (`/etc/systemd/system/mastodon-*.service`), which may contain your server tweaks and customizations -* The pgbouncer configuration under `/etc/pgbouncer` (if you’re using it) +* The PgBouncer configuration under `/etc/pgbouncer` (if you’re using it) -### Dump and load Postgres {#dump-and-load-postgres} +### Dump and load PostgreSQL {#dump-and-load-postgresql} -Instead of running `mastodon:setup`, we’re going to create an empty Postgres database using the `template0` database (which is useful when restoring a Postgres dump, [as described in the pg_dump documentation](https://www.postgresql.org/docs/9.1/static/backup-dump.html#BACKUP-DUMP-RESTORE)). +Instead of running `mastodon:setup`, we’re going to create an empty PostgreSQL database using the `template0` database (which is useful when restoring a PostgreSQL dump, [as described in the pg_dump documentation](https://www.postgresql.org/docs/9.1/static/backup-dump.html#BACKUP-DUMP-RESTORE)). Run this as the `mastodon` user on your old system: @@ -80,7 +80,7 @@ You’ll want to re-run this if any of the files on the old server change. You should also copy over the `.env.production` file, which contains secrets. -Optionally, you may copy over the nginx, systemd, and pgbouncer config files, or rewrite them from scratch. +Optionally, you may copy over the nginx, systemd, and PgBouncer config files, or rewrite them from scratch. ### During migration {#during-migration} diff --git a/content/en/admin/moderation.md b/content/en/admin/moderation.md index ebf15e52..d39735f8 100644 --- a/content/en/admin/moderation.md +++ b/content/en/admin/moderation.md @@ -15,31 +15,33 @@ As of v3.5.0, all default user moderation decisions will notify the affected use ### Sensitive {#sensitive-user} -When an account is marked as sensitive, all media that user posts will be automatically [marked as sensitive](https://docs.joinmastodon.org/user/posting/#cw). +When an account is marked as sensitive, all media that the user posts will be automatically [marked as sensitive](https://docs.joinmastodon.org/user/posting/#cw). ### Freeze {#freeze-user} -A Mastodon account can be frozen. This prevents the user from doing anything with the account, but all of the content is still there untouched. This limitation is reversible; the account can be un-frozen at any time. This limitation is only available for local users on your server. +A Mastodon account can be frozen. This prevents the user from doing anything with the account, but all of the content is still there untouched. This limitation is reversible; the account can be unfrozen at any time. This limitation is only available for local users on your server. When a user's account is frozen, they are redirected to their **Account Settings** page, where the following message is displayed: > You can no longer login to your account or use it in any other way, but your profile and other data remains intact. -When the user's account is un-frozen, normal functionality resumes. +When the user's account is unfrozen, normal functionality resumes. ### Limit {#limit-user} -Previously known as "silencing". A limited account is hidden to all other users on that instance, except for its followers. All of the content is still there, and it can still be found via search, mentions, and following, but the content is invisible publicly. +Previously known as "silencing". A limited account is hidden from all other users on that instance, except for its followers. All of the content is still there, and it can still be found via search, mentions, and following, but the content is invisible publicly. At this moment, limit does not affect federation. A locally limited account is *not* limited automatically on other servers. Account limitations are reversible. ### Suspend {#suspend-user} -A Mastodon suspension means the account is effectively deleted. The account no longer appears in search, the profile page is gone, all of the posts, uploads, followers, and all other data is removed publicly. However, all the data is available in the admin back-end for a period of 30 days from suspension. This is to give the user an opportunity to work with instance admins to resolve any potential issues and have the account re-instated. +A Mastodon suspension means the account is effectively deleted. The account no longer appears in search, the profile page is gone, and all of the posts, uploads, followers, and all other data are removed publicly. However, all the data is available in the admin back-end for a period of 30 days from suspension. This is to give the user an opportunity to work with instance admins to resolve any potential issues and have the account re-instated. -If the account is reinstated within the 30 day period, all data is once again accessible publicly without any adverse effects. If the 30 day period lapses, **all** that user's data is purged from the instance. Admins also have the option to immediately delete the user's account data at any point during the 30 days. +If the account is reinstated within the 30-day period, all data is once again accessible publicly without any adverse effects. If the 30-day period lapses, **all** that user's data is purged from the instance. Admins also have the option to immediately delete the user's account data at any point during the 30 days. -Once the data has been deleted, whether that is after the 30 day period, or if an admin has force deleted it, the account can still be un-suspended. However, the account will have no data (statuses, profile information, avatar or header image) associated with it. +Once the data has been deleted, whether that is after the 30-day period, or if an admin has force deleted it, the account can still be un-suspended. However, the account will have no data (statuses, profile information, avatar or header image) associated with it. + +For remote accounts, suspending will make them unfollow any local account. Those relationships are not restored in case the remote account is unsuspended, even within the 30-day time window. ## Moderating entire websites {#server-wide-moderation} @@ -57,6 +59,8 @@ Equivalent to [limiting](#limit-user) all past and future accounts from the serv Equivalent to [suspending](#suspend-user) all past and future accounts from the server. No content from the server will be stored locally except for usernames. +Suspending a server will remove all existing follow relationships between local accounts and accounts on the suspended server. They will not be restored in case the remote server is un-suspended later. + ## Spam-fighting measures {#spam-fighting-measures} There are a few baseline measures for preventing spam in Mastodon: @@ -64,7 +68,7 @@ There are a few baseline measures for preventing spam in Mastodon: * Signing up requires confirming an e-mail address * Signing up is rate-limited by IP -However, dedicated spammers will get through that. The other measure you can employ is **e-mail domain blacklisting**. During sign up, Mastodon resolves the given e-mail address for an A or MX record, i.e. the IP address of the e-mail server, and checks that IP address against a dynamically stored blacklist. +However, dedicated spammers will get through that. The other measure you can employ is **e-mail domain blacklisting**. During sign-up, Mastodon resolves the given e-mail address for an A or MX record, i.e. the IP address of the e-mail server, and checks that IP address against a dynamically stored blacklist. ### Blocking by e-mail server {#blocking-by-e-mail-server} @@ -72,7 +76,7 @@ Spammers will often use different e-mail domains so it looks like they are using ### Blocking by IP {#blocking-by-ip} -It is not possible to block visitors by IP address in Mastodon itself, and it is not a fool-proof strategy. IPs are sometimes shared by a lot of different people, and sometimes change hands. But it is possible to block visitors by IP address in Linux using a firewall. Here is an example using `iptables` and `ipset`: +It is not possible to block visitors by IP address in Mastodon itself, and it is not a foolproof strategy. IPs are sometimes shared by a lot of different people and sometimes change hands. However, it is possible to block visitors by IP address in Linux using a firewall. Here is an example using `iptables` and `ipset`: ```bash # Install ipset @@ -86,3 +90,229 @@ sudo iptables -I INPUT 1 -m set --match-set spambots src -j DROP ``` Be careful not to lock yourself out of your machine. + +### Webhooks for moderation-level events {#report-events-webhook} + +Webhooks can be created to facilitate automation through the moderation API by notifying applications about system events in real-time. This also enables integrations with chat apps like Discord, IRC and Slack, helping moderator coordination. + +The X-Hub-Signature header adopted from the WebSub spec can be optionally used to verify that the payloads are authentic. + +Events currently supported: + +* account.approved +* account.created +* account.updated +* report.created +* report.updated +* status.created +* status.updated + + + +#### Example payload: + +```json + +{ + "event":"report.created", + "created_at":"2023-10-26T13:34:00.351Z", + "object":{ + "id":"8437", + "action_taken":false, + "action_taken_at":null, + "category":"violation", + "comment":"", + "forwarded":true, + "created_at":"2023-10-26T13:34:00.348Z", + "updated_at":"2023-10-26T13:34:00.348Z", + "account":{ + "id":"123456789", + "username":"bobisaburger", + "domain":null, + "created_at":"2023-07-13T04:39:22.493Z", + "email":"bobisaburger@emailservice.com", + "ip":"12.34.56.78", + "confirmed":true, + "suspended":false, + "silenced":false, + "sensitized":false, + "disabled":false, + "approved":true, + "locale":"en", + "invite_request":"I would love to be a member of your instance!", + "ips":[ + { + "ip":"12.34.56.78", + "used_at":"2023-07-13T04:45:31.835Z" + }, + { + "ip":"98.76.54.32", + "used_at":"2023-07-13T04:39:22.722Z" + } + ], + "account":{ + "id":"123456789", + "username":"bobisaburger", + "acct":"bobisaburger", + "display_name":"bobisaburger", + "locked":false, + "bot":false, + "discoverable":null, + "group":false, + "created_at":"2023-07-13T00:00:00.000Z", + "note":"", + "url":"https://mastodonwebsite/@bobisaburger", + "uri":"https://mastodonwebsite/users/bobisaburger", + "avatar":"https://locationofavatar.com/image.jpg", + "avatar_static":"https://locationofavatar.com/image.jpg", + "header":"locationofheader.com/image.jpg", + "header_static":"locationofheader.com/image.jpg", + "followers_count":100, + "following_count":200, + "statuses_count":9, + "last_status_at":"2023-08-05", + "noindex":true, + "emojis":[ + + ], + "roles":[ + + ], + "fields":[ + + ] + }, + "role":{ + "id":"-99", + "name":"", + "permissions":"65536", + "color":"", + "highlighted":false + } + }, + "target_account":{ + "id":"123454321", + "username":"cheeseperson", + "domain":"someothermastodonsite.com", + "created_at":"2023-08-20T00:00:00.000Z", + "email":null, + "ip":null, + "confirmed":null, + "suspended":false, + "silenced":false, + "sensitized":false, + "disabled":null, + "approved":null, + "locale":null, + "invite_request":null, + "ips":null, + "account":{ + "id":"123454321", + "username":"cheeseperson", + "acct":"cheeseperson@someothermastodonsite.com", + "display_name":"cheeseperson", + "locked":false, + "bot":false, + "discoverable":false, + "group":false, + "created_at":"2023-08-20T00:00:00.000Z", + "note":"", + "url":"https://someothermastodonsite.com/@cheeseperson", + "uri":"https://someothermastodonsite.com/users/cheeseperson", + "avatar":"https://someothermastadonsite.com/avatars/original/missing.png", + "avatar_static":"https://someothermastadonsite.com/avatars/original/missing.png", + "header":"locationofheader.com/image.jpg", + "header_static":"locationofheader.com/image.jpg", + "followers_count":2, + "following_count":2, + "statuses_count":95, + "last_status_at":"2023-10-26", + "emojis":[ + + ], + "fields":[ + + ] + }, + "role":null + }, + "assigned_account":null, + "action_taken_by_account":null, + "statuses":[ + { + "id":"12345678987654321", + "created_at":"2023-10-26T11:29:13.000Z", + "in_reply_to_id":"1918282746465", + "in_reply_to_account_id":"101010101010", + "sensitive":false, + "spoiler_text":"", + "visibility":"public", + "language":"de", + "uri":"https://someothermastodonsite.com/users/cheeseperson/statuses/111301083360371621", + "url":"https://someothermastodonsite.com/@cheeseperson/111301083360371621", + "replies_count":0, + "reblogs_count":0, + "favourites_count":0, + "edited_at":"2023-10-26T11:30:31.000Z", + "content":"

Here is some content

", + "reblog":null, + "account":{ + "id":"123454321", + "username":"cheeseperson", + "acct":"cheeseperson@someothermastodonsite.com", + "display_name":"cheeseperson", + "locked":false, + "bot":false, + "discoverable":false, + "group":false, + "created_at":"2023-08-20T00:00:00.000Z", + "note":"", + "url":"https://someothermastodonsite.com/@cheeseperson", + "uri":"https://someothermastodonsite.com/users/cheeseperson", + "avatar":"https://someothermastadonsite.com/avatars/original/missing.png", + "avatar_static":"https://someothermastadonsite.com/avatars/original/missing.png", + "header":"locationofheader.com/image.jpg", + "header_static":"locationofheader.com/image.jpg", + "followers_count":2, + "following_count":2, + "statuses_count":95, + "last_status_at":"2023-10-26", + "emojis":[ + + ], + "fields":[ + + ] + }, + "media_attachments":[ + + ], + "mentions":[ + { + "id":"101010101010", + "username":"thirdperson", + "url":"https://thirdpersonsinstance.com/@thirdperson", + "acct":"thirdperson@emailwebsite.com" + } + ], + "tags":[ + + ], + "emojis":[ + + ], + "card":null, + "poll":null + } + ], + "rules":[ + { + "id":"2", + "text":"Don't be a meanie!" + } + ] + } +} + + +``` diff --git a/content/en/admin/optional.md b/content/en/admin/optional.md index 3439f0a1..2b15bae7 100644 --- a/content/en/admin/optional.md +++ b/content/en/admin/optional.md @@ -9,6 +9,6 @@ menu: Mastodon offers a few optional features that can be used if needed. -- [Full-text search](./elasticsearch/) +- [Object storage](./object-storage/) - [Hidden services](./tor/) - [Single Sign On](./sso/) diff --git a/content/en/admin/optional/elasticsearch.md b/content/en/admin/optional/elasticsearch.md deleted file mode 100644 index e6c52bf6..00000000 --- a/content/en/admin/optional/elasticsearch.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -title: Full-text search -description: Setting up ElasticSearch to search for statuses authored, favourited, or mentioned in. -menu: - docs: - weight: 10 - parent: admin-optional ---- - -Mastodon supports full-text search when ElasticSearch is available. Mastodon’s full-text search allows logged in users to find results from their own statuses, their mentions, their favourites, and their bookmarks. It deliberately does not allow searching for arbitrary strings in the entire database. - -## Installing ElasticSearch {#install} - -ElasticSearch requires a Java runtime. If you don’t have Java already installed, do it now. Assuming you are logged in as `root`: - -```bash -apt install openjdk-17-jre-headless -``` - -Add the official ElasticSearch repository to apt: - -```bash -wget -O /usr/share/keyrings/elasticsearch.asc https://artifacts.elastic.co/GPG-KEY-elasticsearch -echo "deb [signed-by=/usr/share/keyrings/elasticsearch.asc] https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elastic-7.x.list -``` - -Now you can install ElasticSearch: - -```bash -apt update -apt install elasticsearch -``` - -{{< hint style="warning" >}} -**Security warning:** By default, ElasticSearch is supposed to bind to localhost only, i.e. be inaccessible from the outside network. You can check which address ElasticSearch binds to by looking at `network.host` within `/etc/elasticsearch/elasticsearch.yml`. Consider that anyone who can access ElasticSearch can access and modify any data within it, as there is no authentication layer. So it’s really important that the access is secured. Having a firewall that only exposes the 22, 80 and 443 ports is advisable, as outlined in the [main installation instructions](../../prerequisites/#install-a-firewall-and-only-whitelist-ssh-http-and-https-ports). If you have a multi-host setup, you must know how to secure internal traffic. -{{< /hint >}} - -{{< hint style="danger" >}} -**Security warning:** ElasticSearch versions between `2.0` and `2.14.1` are affected by an [exploit](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228) in the `log4j` library. If affected, please refer to the [temporary mitigation](https://github.com/elastic/elasticsearch/issues/81618#issuecomment-991000240) from the ElasticSearch issue tracker. -{{< /hint >}} - -To start ElasticSearch: - -```bash -systemctl daemon-reload -systemctl enable --now elasticsearch -``` - -## Configuring Mastodon {#config} - -Edit `.env.production` to add the following variables: - -```bash -ES_ENABLED=true -ES_HOST=localhost -ES_PORT=9200 -``` - -If you have multiple Mastodon servers on the same machine, and you are planning to use the same ElasticSearch installation for all of them, make sure that all of them have unique `REDIS_NAMESPACE` in their configurations, to differentiate the indices. If you need to override the prefix of the ElasticSearch indices, you can set `ES_PREFIX` directly. - -After saving the new configuration, restart Mastodon processes for it to take effect: - -```bash -systemctl restart mastodon-sidekiq -systemctl reload mastodon-web -``` - -Now it's time to create the ElasticSearch indices and fill them with data: - -```bash -su - mastodon -cd live -RAILS_ENV=production bin/tootctl search deploy -``` - -## Search optimization for other languages -### Chinese search optimization {#chinese-search-optimization} - -The default analyzer of the ElasticSearch is the standard analyzer, which may not be the best especially for Chinese. To improve search experience, you can install a language specific analyzer. Before creating the indices in ElasticSearch, install the following ElasticSearch extensions: - -- [elasticsearch-analysis-ik](https://github.com/medcl/elasticsearch-analysis-ik) -- [elasticsearch-analysis-stconvert](https://github.com/medcl/elasticsearch-analysis-stconvert) - -And then modify Mastodon's index definition as follows: - -```diff -diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb ---- a/app/chewy/accounts_index.rb -+++ b/app/chewy/accounts_index.rb -@@ -4,7 +4,7 @@ class AccountsIndex < Chewy::Index - settings index: { refresh_interval: '5m' }, analysis: { - analyzer: { - content: { -- tokenizer: 'whitespace', -+ tokenizer: 'ik_max_word', - filter: %w(lowercase asciifolding cjk_width), - }, - -diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb ---- a/app/chewy/statuses_index.rb -+++ b/app/chewy/statuses_index.rb -@@ -16,9 +16,17 @@ class StatusesIndex < Chewy::Index - language: 'possessive_english', - }, - }, -+ char_filter: { -+ tsconvert: { -+ type: 'stconvert', -+ keep_both: false, -+ delimiter: '#', -+ convert_type: 't2s', -+ }, -+ }, - analyzer: { - content: { -- tokenizer: 'uax_url_email', -+ tokenizer: 'ik_max_word', - filter: %w( - english_possessive_stemmer - lowercase -@@ -27,6 +35,7 @@ class StatusesIndex < Chewy::Index - english_stop - english_stemmer - ), -+ char_filter: %w(tsconvert), - }, - }, - } -diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb ---- a/app/chewy/tags_index.rb -+++ b/app/chewy/tags_index.rb -@@ -2,10 +2,19 @@ - - class TagsIndex < Chewy::Index - settings index: { refresh_interval: '15m' }, analysis: { -+ char_filter: { -+ tsconvert: { -+ type: 'stconvert', -+ keep_both: false, -+ delimiter: '#', -+ convert_type: 't2s', -+ }, -+ }, - analyzer: { - content: { -- tokenizer: 'keyword', -+ tokenizer: 'ik_max_word', - filter: %w(lowercase asciifolding cjk_width), -+ char_filter: %w(tsconvert), - }, - - edge_ngram: { -``` - diff --git a/content/en/admin/optional/object-storage-proxy.md b/content/en/admin/optional/object-storage-proxy.md index b29842cc..66ad68b6 100644 --- a/content/en/admin/optional/object-storage-proxy.md +++ b/content/en/admin/optional/object-storage-proxy.md @@ -3,12 +3,12 @@ title: Proxying object storage through nginx description: Serving user-uploaded files in Mastodon from your own domain --- -When you are using Mastodon with an object storage provider like Amazon S3, Wasabi, Google Cloud or other, by default the URLs of the files go through the storage providers themselves. This has the following downsides: +When you are using Mastodon with an object storage provider like Amazon S3, Wasabi, Google Cloud or others, by default the URLs of the files go through the storage providers themselves. This has the following downsides: - Bandwidth is usually metered and very expensive - URLs will be broken if you decide to switch providers later -You can instead serve the files from your own domain, caching them in the process. Access patterns on Mastodon are such that **new files are usually accessed simultaneously by a lot of clients** as new posts stream in through the streaming API or as they get distributed through federation; older content is accessed comparatively rarely. For that reason, caching alone would not reduce bandwidth consumed by your proxy from the actual object storage. To mitigate this, we can use a **cache lock** mechanism that ensures that only one proxy request is made at the same time. +You can choose to serve the files from your own domain, incorporating caching in the process. In Mastodon, access patterns show that new files are often simultaneously accessed by many clients as they appear in new posts via the streaming API or are shared through federation; in contrast, older content is accessed less frequently. Therefore, relying solely on caching won't significantly reduce the bandwidth usage of your proxy from the actual object storage. To address this, we can implement a cache lock mechanism, which ensures that only one proxy request is made at a time. Here is an example nginx configuration that accomplishes this: @@ -19,6 +19,9 @@ server { server_name files.example.com; root /var/www/html; + ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; + ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; + keepalive_timeout 30; location = / { @@ -37,7 +40,7 @@ server { } resolver 8.8.8.8; - proxy_set_header Host YOUR_S3_HOSTNAME; + proxy_set_header Host YOUR_BUCKET_NAME.YOUR_S3_HOSTNAME; proxy_set_header Connection ''; proxy_set_header Authorization ''; proxy_hide_header Set-Cookie; @@ -63,6 +66,8 @@ server { add_header Cache-Control public; add_header 'Access-Control-Allow-Origin' '*'; add_header X-Cache-Status $upstream_cache_status; + add_header X-Content-Type-Options nosniff; + add_header Content-Security-Policy "default-src 'none'; form-action 'none'"; } } ``` @@ -97,7 +102,7 @@ ln -s /etc/nginx/sites-available/files.example.com /etc/nginx/sites-enabled/ systemctl reload nginx ``` -You'll also want to get a SSL certificate for it: +You'll also want to get an SSL certificate for it: ```bash certbot --nginx -d files.example.com diff --git a/content/en/admin/optional/object-storage.md b/content/en/admin/optional/object-storage.md new file mode 100644 index 00000000..3a48ae0d --- /dev/null +++ b/content/en/admin/optional/object-storage.md @@ -0,0 +1,232 @@ +--- +title: Configuring object storage +description: Serving user-uploaded files in Mastodon using external object storage +menu: + docs: + weight: 15 + parent: admin-optional +--- + +User-uploaded files can be stored on the main server's file system, or using an external object storage server, which can be required for scaling. + +## Using the filesystem {#FS} + +The simplest way to store user uploads is by using the server's file system. This is how it works by default and is suitable for small servers. + +By default, Mastodon will store file uploads under `public/system` in its installation directory, but that can be overridden using the `PAPERCLIP_ROOT_PATH` environment variable. + +By default, the files are served at `https://your-domain/system`, which can be overridden using `PAPERCLIP_ROOT_URL` and `CDN_HOST`. + +{{< hint style="info" >}} +While using the server's file system is perfectly serviceable for small servers, using external object storage is more scalable. +{{}} + +{{< hint style="danger" >}} +The web server must be configured to serve those files but not allow listing them (that is, `https://your-domain/system/` should not return a file list). This should be the case if you use the configuration files distributed with Mastodon, but it is worth double-checking. +{{}} + +## S3-compatible object storage backends {#S3} + +Mastodon can use S3-compatible object storage backends. ACL support is recommended as it allows Mastodon to quickly make the content of temporarily suspended users unavailable, or marginally improve the security of private data. + +On Mastodon's end, you need to configure the following environment variables: +- `S3_ENABLED=true` +- `S3_BUCKET=mastodata` (replacing `mastodata` with the name of your bucket) +- `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` need to be set to your credentials +- `S3_ALIAS_HOST` is optional but highly recommended in order to set up a caching proxy and not lock you to a specific provider +- `S3_REGION` +- `S3_HOSTNAME` (optional if you use Amazon AWS) +- `S3_PERMISSION` (optional, if you use a provider that does not support ACLs or want to use custom ACLs) +- `S3_FORCE_SINGLE_REQUEST=true` (optional, if you run into trouble processing large files) + +{{< page-ref page="admin/optional/object-storage-proxy.md" >}} + +{{< hint style="info" >}} +You must serve the files with CORS headers, otherwise some functions of Mastodon's web UI will not work. For example, `Access-Control-Allow-Origin: *` +{{}} + +{{< hint style="danger" >}} +Regardless of the ACL configuration, your S3 bucket must be set up to ensure that all objects are publicly readable but not writable or listable. At the same time, Mastodon itself should have write access to the bucket. This configuration is generally consistent across all S3 providers, and common ones are highlighted below. +{{}} + +### MinIO + +MinIO is an open-source implementation of an S3 object provider. This section does not cover how to install it, but how to configure a bucket for use in Mastodon. + +You need to set a policy for anonymous access that allows read-only access to objects contained by the bucket without allowing listing them. + +To do this, you need to set a custom policy (replace `mastodata` with the actual name of your S3 bucket): +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "*" + }, + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::mastodata/*" + } + ] +} +``` + +Mastodon itself needs to be able to write to the bucket, so either use your admin MinIO account (discouraged) or an account specific to Mastodon (recommended) with the following policy attached (replace `mastodata` with the actual name of your S3 bucket): +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "s3:*", + "Resource": "arn:aws:s3:::mastodata/*" + } + ] +} +``` + +You can set those policies from the MinIO Console (web-based user interface) or the command-line client (`mcli` / `mc`). + +#### Using the MinIO Console + +Connect to the MinIO Console web interface and create a new bucket (or navigate to your existing bucket): +![](/assets/object-storage/minio-bucket.png) + +Then, configure the “Access Policy” to a custom one that allows read access (`s3:GetObject`) without write access or the ability to list objects (see above): +![](/assets/object-storage/minio-access-policy.png) + +{{< hint style="info" >}} +If the MinIO Console does not allow you to set a “Custom” policy, you will likely need to update MinIO. If you are using MinIO in *standalone* or *filesystem* mode, [`RELEASE.2022-10-24T18-35-07Z`](https://github.com/minio/minio/releases/tag/RELEASE.2022-10-24T18-35-07Z) should be a safe version to update to that does not require [an involved migration procedure](https://min.io/docs/minio/linux/operations/install-deploy-manage/migrate-fs-gateway.html#migrate-from-gateway-or-filesystem-mode). +{{< /hint >}} + +Create a new `mastodon-readwrite` policy (see above): +![](/assets/object-storage/minio-mastodon-readwrite.png) + +Finally, create a new `mastodon` user with the `mastodon-readwrite` policy: +![](/assets/object-storage/minio-mastodon-user.png) + +#### Using the command-line utility + +The same can be achieved using the [MinIO Client](https://min.io/docs/minio/linux/reference/minio-mc.html) command-line utility (which can be called `mc` or `mcli` depending on where it is installed from). + +Create a new bucket: +`mc mb myminio/mastodata` + +Save the anonymous access policy from above as `anonymous-readonly-policy.json` and the Mastodon user access policy as `mastodon-readwrite.json` (make sure to replace `mastodata` with the name of your newly-created bucket). + +Set the anonymous access policy for your bucket: +`mc anonymous set-json anonymous-readonly-policy.json myminio/mastodata` + +Add a `mastodon-readwrite` policy: +`mc admin policy add myminio mastodon-readwrite mastodon-readwrite.json` + +Add the `mastodon` user (replace the password): +`mc admin user add myminio mastodon SECRET_PASSWORD` + +Apply the `mastodon-readwrite` policy to the `mastodon` user: +`mc admin policy set myminio mastodon-readwrite user=mastodon` + +### Wasabi Object Storage + +Create a new bucket and define its policy to allow objects to be anonymously readable but not listable: +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "*" + }, + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::mastodata/*" + } + ] +} +``` + +![](/assets/object-storage/wasabi-access-policy.png) + +{{< hint style="info" >}} +If you are using an old bucket, ensure you are not giving “Everyone” read access to objects through Wasabi's legacy Access Control settings, as that allows listing objects and take precedence over the IAM policy defined above. + +![](/assets/object-storage/wasabi-access-control.png) +{{< /hint >}} + +Then, create a `mastodon-readwrite` policy to grant read and write access to your bucket: +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "s3:*", + "Resource": "arn:aws:s3:::mastodata/*" + } + ] +} +``` + +![](/assets/object-storage/wasabi-mastodon-readwrite.png) + +Finally, create a new `mastodon` user and don't forget to enable the `mastodon-readwrite` policy: +![](/assets/object-storage/wasabi-mastodon-user.png) + +On Mastodon's side, you need to set `S3_FORCE_SINGLE_REQUEST=true` to properly handle large uploads. + +### DigitalOcean Spaces + +In your DigitalOcean Spaces Bucket, make sure that “File Listing” is “Restricted” to users with access keys. + +![](/assets/object-storage/do-spaces.png) + +### Scaleway + +If you want to use Scaleway Object Storage, we strongly recommend you create a Scaleway project dedicated to your Mastodon instance assets and use a custom IAM policy. + +First, create a new Scaleway project, in which you create your object storage bucket. You need to set your bucket visibility to "Private" to not allow objects to be listed. + +![](/assets/object-storage/scaleway-bucket.png) + +Now that your bucket is created, you need to create API keys to be used in your Mastodon instance configuration. + +Head to the IAM settings (in your organization menu, top right of the screen), and create a new IAM policy (eg `mastodon-media-access`) + +![](/assets/object-storage/scaleway-policy.jpg) + +This policy needs to have one rule, allowing it to read, write and delete objects in the Scaleway project you created above (the scope). + +![](/assets/object-storage/scaleway-policy-rules.jpg) + +Then head to the IAM Applications page, and create a new one (eg `my-mastodon-instance`) and select the policy you created above. + +Finally, click on the application you just created, then "API Keys", and create a new API key to use in your instance configuration. You should use the "Yes, set up preferred Project" option and select the project you created above as the default project for this key. + +![](/assets/object-storage/scaleway-api-key.png) + +Copy the Access Key ID and Secret, and use them for your `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` Mastodon config variables. + +### Exoscale + +In Exoscale, your bucket should not have any read ACLs (Mastodon will set the ACLs on the object themselves as appropriate). + +You need to create an API Key for the Mastodon app, restricted to the Object Storage (`sos`) service, restricted to your bucket, and with unrestricted operations. + +![](/assets/object-storage/exoscale.png) + +On Mastodon's side, you need to set `S3_FORCE_SINGLE_REQUEST=true` to properly handle large uploads. + +### Cloudflare R2 + +Cloudflare R2 does not support ACLs, so Mastodon needs to be instructed not to try setting them. To do that, set the `S3_PERMISSION` environment variable to an empty string. + +{{< hint style="warning" >}} +Without support for ACLs, media files from temporarily-suspended users will remain accessible. +{{< /hint >}} + +To get credentials for use in Mastodon, select “Manage R2 API Tokens” and create a new API token with “Edit” permissions. + +{{< hint style="warning" >}} +This section is currently under construction. +{{< /hint >}} diff --git a/content/en/admin/optional/tor.md b/content/en/admin/optional/tor.md index 67c1d795..d4e2efc6 100644 --- a/content/en/admin/optional/tor.md +++ b/content/en/admin/optional/tor.md @@ -1,6 +1,6 @@ --- -title: Hidden services -description: Serving Mastodon through TOR hidden services. +title: Onion services +description: Serving Mastodon through Tor onion services. menu: docs: weight: 20 @@ -36,24 +36,24 @@ apt install tor deb.torproject.org-keyring Edit the file at `/etc/tor/torrc` and add the following configuration. ```text -HiddenServiceDir /var/lib/tor/hidden_service/ +HiddenServiceDir /var/lib/tor/onion_service/ HiddenServiceVersion 3 HiddenServicePort 80 127.0.0.1:80 ``` -Restart tor. +Restart Tor. ```bash sudo service tor restart ``` -Your tor hostname can now be found at `/var/lib/tor/hidden_service/hostname`. +You can now find your Tor hostname in `/var/lib/tor/hidden_service/hostname`. ## Move your Mastodon configuration {#nginx} -We will need to tell Nginx about your Mastodon configuration twice. To keep things [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) we need to move the Mastodon configuration into its own file that can be referenced. +We will need to tell Nginx about your Mastodon configuration twice. To keep things ["DRY"](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) we need to move the Mastodon configuration into its own file that can we can refer to later. -Create a new file at `/etc/nginx/snippets/mastodon.conf`. Put all of your Mastodon configuration parameters in this file with the exception of the `listen`, `server_name`, `include` and all of the SSL options. Your new file may look something like this. +Create a new file at `/etc/nginx/snippets/mastodon.conf`. Copy every Mastodon configuration parameter, apart from the `listen`, `server_name`, `include` directives, as well as all of the SSL options. Your new file should look somewhat like this: ```nginx add_header Referrer-Policy "same-origin"; @@ -72,14 +72,14 @@ access_log /var/log/nginx/mastodon_access.log; error_log /var/log/nginx/mastodon_error.log warn; ``` -In place of your old Mastodon configuration add an include directive to this new configuration file. +In the new configuration file, add an include directive in the place of where your Mastodon configurations were. -Your Nginx configuration file will be left looking something like this. +Your Nginx configuration file should now look a bit like this: ```nginx server { listen 80; - server_name mastodon.myhosting.com; + server_name mastodon.example.com; return 301 https://$server_name$request_uri; } @@ -91,19 +91,21 @@ map $http_upgrade $connection_upgrade { server { listen 443 ssl http2; list [::]:443 ssl http2; - server_name mastodon.myhosting.com; + server_name mastodon.example.com; include /etc/nginx/snippets/mastodon.conf; - ssl_certificate /etc/letsencrypt/live/mastodon.myhosting.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/mastodon.myhosting.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/mastodon.example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/mastodon.example.com/privkey.pem; } ``` ## Serve Tor over http {#http} -While it may be tempting to serve your Tor version of Mastodon over https it is not a good idea for most people. See [this](https://blog.torproject.org/facebook-hidden-services-and-https-certs) blog post from the Tor Project about why https certificates do not add value. Since you cannot get an SSL cert for an onion domain, you will also be plagued with certificate errors when trying to use your Mastodon instance. A Tor developer has more recently spelled out the reasons why serving a Tor service over https is not beneficial for most use cases [here](https://matt.traudt.xyz/posts/2017-12-02-dont-https-your-onions/). +This section assumes that you want to expose your instance on both Tor and the public Internet *simultaneously*. -The solution is to serve your Mastodon instance over http, but only for Tor. This can be added by prepending an additional configuration to your Nginx configuration. +While it may be tempting to serve your Tor version of Mastodon over HTTPS, it isn't always ideal. They are mostly useful for large companies that can produce their own certificates with their own company information. There is no Certificate Authority (CA) that provides them [for free](https://community.torproject.org/onion-services/advanced/https/), and there is also [a blog post from the Tor Project](https://blog.torproject.org/facebook-hidden-services-and-https-certs) explains why HTTPS certificates are not really beneficial for security. On the other hand, however, Mastodon uses a lot of redirects to the HTTPS version of your site, where the presence of a validated certificate may make it easier for your users to use your instance on Tor without having to manually remove the `https://` prefix in URLs. + +In this section, we will go over how to serve your Mastodon instance over HTTP, but for Tor **only**. This can be added by prepending an additional configuration to your existing Nginx configuration. ```nginx server { @@ -114,7 +116,7 @@ server { server { listen 80; - server_name mastodon.myhosting.com; + server_name mastodon.example.com; return 301 https://$server_name$request_uri; } @@ -126,19 +128,19 @@ map $http_upgrade $connection_upgrade { server { listen 443 ssl http2; list [::]:443 ssl http2; - server_name mastodon.myhosting.com; + server_name mastodon.example.com; include /etc/nginx/snippets/mastodon.conf; - ssl_certificate /etc/letsencrypt/live/mastodon.myhosting.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/mastodon.myhosting.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/mastodon.example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/mastodon.example.com/privkey.pem; } ``` Replace the long hash provided here with your Tor domain located in the file at `/var/lib/tor/hidden_service/hostname`. -Note that the onion hostname has been prefixed with “mastodon.”. Your Tor address acts a wildcard domain. All subdomains will be routed through, and you can configure Nginx to respond to any subdomain you wish. If you do not wish to host any other services on your tor address you can omit the subdomain, or choose a different subdomain. +Note that the onion hostname has been prefixed with “mastodon.”. Your Tor address acts as a wildcard domain. All subdomains will be routed through, and you can configure Nginx to respond to any subdomain you wish. If you do not wish to host any other services on your tor address you can omit the subdomain, or choose a different subdomain. -Here you can see the payoff of moving your mastodon configurations to a different file. Without this all of your configurations would have to be copied to both places. Any change to your configuration would have to be made both places. +Here you can see the payoff of moving your mastodon configurations to a different file. Without this, all of your configurations would have to be copied to both places. Any change to your configuration would have to be made in both places. Restart your web server. @@ -148,7 +150,7 @@ service nginx restart ## Gotchas {#gotchas} -There are a few things you will need to be aware of. Certain redirects will push your users to https. They will have to manually replace the URL with http to continue. - -Various resources, such as images, will still be offered through your regular non-Tor domain. How much of a problem this is will depend greatly on your user’s level of caution. +There are a few things you will need to be aware of. +- As mentioned earlier, certain URLs in the Mastodon frontend will force your users to a HTTPS URL. They will have to manually replace the URL with HTTP to continue. +- Various resources, such as images, will **still** be offered through your regular clearnet domain. This could possibly be a problem, depending on how cautious your users want, try or need to be. diff --git a/content/en/admin/prerequisites.md b/content/en/admin/prerequisites.md index d2457599..691d53b0 100644 --- a/content/en/admin/prerequisites.md +++ b/content/en/admin/prerequisites.md @@ -10,7 +10,7 @@ If you are setting up a fresh machine, it is recommended that you secure it firs ## Do not allow password-based SSH login (keys only) -First make sure you are actually logging in to the server using keys and not via a password, otherwise this will lock you out. Many hosting providers support uploading a public key and automatically set up key-based root login on new machines for you. +First, make sure you are actually logging in to the server using keys and not via a password, otherwise, this will lock you out. Many hosting providers support uploading a public key and automatically set up key-based root login on new machines for you. Edit `/etc/ssh/sshd_config` and find `PasswordAuthentication`. Make sure it’s uncommented and set to `no`. If you made any changes, restart sshd: @@ -42,13 +42,10 @@ sendername = Fail2Ban [sshd] enabled = true port = 22 - -[sshd-ddos] -enabled = true -port = 22 +mode = aggressive ``` -Finally restart fail2ban: +Finally, restart fail2ban: ```bash systemctl restart fail2ban @@ -56,7 +53,7 @@ systemctl restart fail2ban ## Install a firewall and only allow SSH, HTTP and HTTPS ports -First, install iptables-persistent. During installation it will ask you if you want to keep current rules–decline. +First, install iptables-persistent. During installation, it will ask you if you want to keep the current rules–decline. ```bash apt install -y iptables-persistent @@ -80,6 +77,8 @@ Edit `/etc/iptables/rules.v4` and put this inside: # Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL). -A INPUT -p tcp --dport 80 -j ACCEPT -A INPUT -p tcp --dport 443 -j ACCEPT +# (optional) Allow HTTP/3 connections from anywhere. +-A INPUT -p udp --dport 443 -j ACCEPT # Allow SSH connections # The -dport number should be the same port number you set in sshd_config @@ -124,6 +123,8 @@ If your server is also reachable over IPv6, edit `/etc/iptables/rules.v6` and ad # Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL). -A INPUT -p tcp --dport 80 -j ACCEPT -A INPUT -p tcp --dport 443 -j ACCEPT +# (optional) Allow HTTP/3 connections from anywhere. +-A INPUT -p udp --dport 443 -j ACCEPT # Allow SSH connections # The -dport number should be the same port number you set in sshd_config diff --git a/content/en/admin/roles.md b/content/en/admin/roles.md new file mode 100644 index 00000000..079ebdfb --- /dev/null +++ b/content/en/admin/roles.md @@ -0,0 +1,98 @@ +--- +title: Roles +description: Management of roles from the admin dashboard. +menu: + docs: + parent: admin +--- + +# Roles {#roles} +When the database is seeded, roles are derived from the values present in [`~/config/roles.yml`](https://github.com/mastodon/mastodon/blob/main/config/roles.yml). + +{{< page-ref page="entities/Role" >}} + +The resultant [default roles](#default-roles) are `Owner`, `Admin`, and `Moderator`. + +A role and its attributes can be created using [Add role](#add-role), present on the *Roles* (`/admin/roles`) page. + +![](/assets/admin-roles-ui.png) + +An existing role's attributes can be changed using the [edit role](#edit-role) feature. + +## Default roles {#default-roles} +### Base role (*Default permissions*) {#default-base-role} + +Affects all users, including users without an assigned role. + +The only permission flag that can be altered for this role is **Invite Users**. Enabling this permission allows all users to send invitations. + +The base role has a priority of `0`, and this value cannot be altered. + +### Owner {#default-owner-role} + +A role that is assigned the **Administrator** permission flag, bypassing all permissions. Users with the owner role have every [permission flag](/entities/Role/#permission-flags) enabled. + +The role's *Name*, *Badge color*, and *Display badge* attributes can be changed. No permissions can be edited / revoked from this role. + +The owner role has the highest [priority](#role-priority) of any role (`1000`). The owner can modify any other role attributes. No role can be created which supersedes the owner role, as [role priority](#role-priority) for new and existing roles must be <= `999`. + +### Admin {#default-admin-role} + +A role that is assigned all **Moderation** and **Administration** permission flags. + +The **DevOps** permission flag for this role is disabled, but can be enabled by an **Owner** (or a custom role with a higher priority value). + +The role's *Name*, *Badge color*, and *Display badge* attributes can be changed. + +The admin role has a priority of `100`. + +### Moderator {#default-moderator-role} + +A role that is assigned certain **Moderation** permission flags. These include... +- **View Dashboard** +- **View Audit Log** +- **Manage Users** +- **Manage Reports** +- **Manage Taxonomies** + +The role's *Name*, *Badge color*, and *Display badge* attributes can be changed. + +The moderator role has a priority of `10`. + +## Add Role {#add-role} + +The `admin/roles/new` page allows for the creation of a custom role. + +![](/assets/admin-roles-new-ui.png) + +### Input Fields {#add-role-input-fields} + +{{< page-relref ref="entities/Role#name" caption="Name">}} + +Duplicate role names can exist. They are discerned in the database by their `id`, which cannot be set from the web interface. + +{{< page-relref ref="entities/Role#color" caption="Badge color">}} + +### Priority {#role-priority} + +- Defaults to `0` + - Cannot be > `999` + - Can be any negative integer value +- Two roles can have the same priority value + +> "Higher role decides conflict resolution in certain situations. Certain actions can only be performed on roles with a lower priority." + +{{< page-relref ref="entities/Role#highlighted" caption="Display role as badge on user profiles">}} + +{{< page-relref ref="entities/Role#permissions" caption="Permissions">}} + + +## Edit role {#edit-role} + +![](/assets/admin-roles-edit-ui.png) + +An existing role and its attributes can be edited using *Edit* in the role list. [Input fields](#add-role-input-fields) can be changed and saved, just as they can when creating a new role. The role can also be deleted using this form. + +![](/assets/admin-roles-edit-role-ui.png) + +A logged in user with permission to **Manage Roles** will always be able to see every role, but cannot modify roles that exceed or are equal to their assigned role's [priority](#role-priority). \ No newline at end of file diff --git a/content/en/admin/scaling.md b/content/en/admin/scaling.md index 3494d69c..434fa1f6 100644 --- a/content/en/admin/scaling.md +++ b/content/en/admin/scaling.md @@ -11,18 +11,18 @@ menu: Mastodon has three types of processes: -* Web (Puma) -* Streaming API -* Background processing (Sidekiq) +- Web (Puma) +- Streaming API +- Background processing (Sidekiq) ### Web (Puma) {#web} The web process serves short-lived HTTP requests for most of the application. The following environment variables control it: -* `WEB_CONCURRENCY` controls the number of worker processes -* `MAX_THREADS` controls the number of threads per process +- `WEB_CONCURRENCY` controls the number of worker processes +- `MAX_THREADS` controls the number of threads per process -Threads share the memory of their parent process. Different processes allocate their own memory, though they share some memory via copy-on-write. A larger number of threads maxes out your CPU first, a larger number of processes maxes out your RAM first. +Threads share the memory of their parent process. Different processes allocate their own memory, though they share some memory via copy-on-write. A larger number of threads maxes out your CPU first, and a larger number of processes maxes out your RAM first. These values affect how many HTTP requests can be served at the same time. @@ -32,10 +32,53 @@ In terms of throughput, more processes are better than more threads. The streaming API handles long-lived HTTP and WebSockets connections, through which clients receive real-time updates. The following environment variables control it: -* `STREAMING_CLUSTER_NUM` controls the number of worker processes -* `STREAMING_API_BASE_URL` controls the base URL of the streaming API +- `STREAMING_API_BASE_URL` controls the base URL of the streaming API +- `PORT` controls the port the streaming server will listen on, by default 4000. The `BIND` and `SOCKET` environment variables are also able to be used. +- Additionally, the shared [database](/admin/config#postgresql) and [redis](/admin/config#redis) environment variables are used. -One process can handle a reasonably high number of connections. The streaming API can be hosted on a different subdomain if you want to e.g. avoid the overhead of nginx proxying the connections. +The streaming API can use a different subdomain if you want to by setting `STREAMING_API_BASE_URL`. This allows you to have one load balancer for streaming and one for web/API requests. However, this also requires applications to correctly request the streaming URL from the [instance endpoint](/methods/instance/#v2), instead of assuming that it's hosted on the same host as the Web API. + +One process of the streaming server can handle a reasonably high number of connections and throughput, but if you find that a single process isn't handling your instance's load, you can run multiple processes by varying the `PORT` number of each, and then using nginx to load balance traffic to each of those instances. For example, a community of about 50,000 accounts with 10,000-20,000 monthly active accounts, you'll typically have an average concurrent load of about 800-1200 streaming connections. + +The streaming server also exposes a [Prometheus](https://prometheus.io/) endpoint on `/metrics` with a lot of metrics to help you understand the current load on your mastodon streaming server, some key metrics are: + +* `mastodon_streaming_connected_clients`: This is the number of connected clients, tagged by client type (websocket or eventsource) +* `mastodon_streaming_connected_channels`: This is the number of "channels" that are currently subscribed (note that this is much higher than connected clients due to how our internal "system" channels currently work) +* `mastodon_streaming_messages_sent_total`: This is the total number of messages sent to clients since last restart. +* `mastodon_streaming_redis_messages_received_total`: This is the number of messages received from Redis pubsub, and intended to complement [monitoring Redis directly](https://sysdig.com/blog/redis-prometheus/). + +{{< hint style="info" >}} +The more streaming server processes that you run, the more database connections will be consumed on PostgreSQL, so you'll likely want to use PgBouncer, as documented below. +{{< /hint >}} + +An example nginx configuration to route traffic to three different processes on `PORT` 4000, 4001, and 4002 is as follows: + +``` +upstream streaming { + least_conn; + server 127.0.0.1:4000 fail_timeout=0; + server 127.0.0.1:4001 fail_timeout=0; + server 127.0.0.1:4002 fail_timeout=0; +} +``` + +If you're using the distributed systemd files, then you can start up multiple streaming servers with the following commands: + +``` +$ sudo systemctl start mastodon-streaming@4000.service +$ sudo systemctl start mastodon-streaming@4001.service +$ sudo systemctl start mastodon-streaming@4002.service +``` + +By default, `sudo systemctl start mastodon-streaming` starts just one process on port 4000, equivalent to running `sudo systemctl start mastodon-streaming@4000.service`. + +{{< hint style="warning" >}} +Previous versions of Mastodon had a `STREAMING_CLUSTER_NUM` environment variable that made the streaming server use clustering, which started mulitple workers processes and used node.js to load balance them. + +This interacted with the other settings in ways which made capacity planning difficult, especially when it comes to database connections and CPU resources. By default the streaming server would consume resources on all available CPUs which could cause contention with other software running on that server. Another common issue was that misconfiguring the `STREAMING_CLUSTER_NUM` would exhaust your database connections by opening up a connection pool per cluster worker process, so a `STREAMING_CLUSTER_NUM` of `5` and `DB_POOL` of `10` would potentially consume 50 database connections. + +Now a single streaming server process will only use at maximum `DB_POOL` PostgreSQL connections, and scaling is handled by running more instances of the streaming server. +{{< /hint >}} ### Background processing (Sidekiq) {#sidekiq} @@ -45,26 +88,26 @@ Many tasks in Mastodon are delegated to background processing to ensure the HTTP While the amount of threads in the web process affects the responsiveness of the Mastodon instance to the end-user, the amount of threads allocated to background processing affects how quickly posts can be delivered from the author to anyone else, how soon e-mails are sent out, etc. -The amount of threads is not controlled by an environment variable in this case, but a command line argument in the invocation of Sidekiq, e.g.: +The number of threads is not regulated by an environment variable, but rather through a command line argument when invoking Sidekiq, as shown in the following example: ```bash bundle exec sidekiq -c 15 ``` -Would start the sidekiq process with 15 threads. Please mind that each threads needs to be able to connect to the database, which means that the database pool needs to be large enough to support all the threads. The database pool size is controlled with the `DB_POOL` environment variable and must be at least the same as the number of threads. +This would initiate the Sidekiq process with 15 threads. It's important to note that each thread requires a database connection, necessitating a sufficiently large database pool. The size of this pool is managed by the DB_POOL environment variable, which should be set to a value at least equal to the number of threads. #### Queues {#sidekiq-queues} Sidekiq uses different queues for tasks of varying importance, where importance is defined by how much it would impact the user experience of your server’s local users if the queue wasn’t working, in order of descending importance: -| Queue | Significance | -| :--- | :--- | -| `default` | All tasks that affect local users | -| `push` | Delivery of payloads to other servers | -| `mailers` | Delivery of e-mails | -| `pull` | Lower priority tasks such as handling imports, backups, resolving threads, deleting users, forwarding replies | -| `scheduler` | Doing cron jobs like refreshing trending hashtags and cleaning up logs | -| `ingress` | Incoming remote activities. Lower priority than the default queue so local users still see their posts when the server is under load | +| Queue | Significance | +| :---------- | :----------------------------------------------------------------------------------------------------------------------------------- | +| `default` | All tasks that affect local users | +| `push` | Delivery of payloads to other servers | +| `mailers` | Delivery of e-mails | +| `pull` | Lower priority tasks such as handling imports, backups, resolving threads, deleting users, forwarding replies | +| `scheduler` | Doing cron jobs like refreshing trending hashtags and cleaning up logs | +| `ingress` | Incoming remote activities. Lower priority than the default queue so local users still see their posts when the server is under load | The default queues and their priorities are stored in [config/sidekiq.yml](https://github.com/mastodon/mastodon/blob/main/config/sidekiq.yml), but can be overridden by the command-line invocation of Sidekiq, e.g.: @@ -74,20 +117,19 @@ bundle exec sidekiq -q default To run just the `default` queue. -The way Sidekiq works with queues, it first checks for tasks from the first queue, and if there are none, checks the next queue. This means, if the first queue is overfilled, the other queues will lag behind. +Sidekiq processes queues by first checking for tasks in the first queue, and if it finds none, it then checks the subsequent queue. Consequently, if the first queue is overfilled, tasks in the other queues may experience delays. As a solution, it is possible to start different Sidekiq processes for the queues to ensure truly parallel execution, by e.g. creating multiple systemd services for Sidekiq with different arguments. **Make sure you only have one `scheduler` queue running!!** - -## Transaction pooling with pgBouncer {#pgbouncer} +## Transaction pooling with PgBouncer {#pgbouncer} ### Why you might need PgBouncer {#pgbouncer-why} -If you start running out of available Postgres connections (the default is 100) then you may find PgBouncer to be a good solution. This document describes some common gotchas as well as good configuration defaults for Mastodon. +If you start running out of available PostgreSQL connections (the default is 100) then you may find PgBouncer to be a good solution. This document describes some common gotchas as well as good configuration defaults for Mastodon. -Note that you can check “PgHero” in the administration view to see how many Postgres connections are currently being used. Typically Mastodon uses as many connections as there are threads both in Puma, Sidekiq and the streaming API combined. +User roles with `DevOps` permissions in Mastodon can monitor the current usage of PostgreSQL connections through the PgHero link in the Administration view. Generally, the number of connections open is equal to the total threads in Puma, Sidekiq, and the streaming API combined. ### Installing PgBouncer {#pgbouncer-install} @@ -101,7 +143,7 @@ sudo apt install pgbouncer #### Setting a password {#pgbouncer-password} -First off, if your `mastodon` user in Postgres is set up without a password, you will need to set a password. +First off, if your `mastodon` user in PostgreSQL is set up without a password, you will need to set a password. Here’s how you might reset the password: @@ -145,20 +187,20 @@ You’ll also want to create a `pgbouncer` admin user to log in to the PgBouncer "pgbouncer" "md5a45753afaca0db833a6f7c7b2864b9d9" ``` -In both cases the password is just `password`. +In both cases, the password is just `password`. #### Configuring pgbouncer.ini {#pgbouncer-ini} Edit `/etc/pgbouncer/pgbouncer.ini` -Add a line under `[databases]` listing the Postgres databases you want to connect to. Here we’ll just have PgBouncer use the same username/password and database name to connect to the underlying Postgres database: +Add a line under `[databases]` listing the PostgreSQL databases you want to connect to. Here we’ll just have PgBouncer use the same username/password and database name to connect to the underlying PostgreSQL database: ```text [databases] mastodon_production = host=127.0.0.1 port=5432 dbname=mastodon_production user=mastodon password=password ``` -The `listen_addr` and `listen_port` tells PgBouncer which address/port to accept connections. The defaults are fine: +The `listen_addr` and `listen_port` tell PgBouncer which address/port to accept connections. The defaults are fine: ```text listen_addr = 127.0.0.1 @@ -167,11 +209,23 @@ listen_port = 6432 Put `md5` as the `auth_type` (assuming you’re using the md5 format in `userlist.txt`): +```ini +auth_type = md5 +``` + Make sure the `pgbouncer` user is an admin: -**This next part is very important!** The default pooling mode is session-based, but for Mastodon we want transaction-based. In other words, a Postgres connection is created when a transaction is created and dropped when the transaction is done. So you’ll want to change the `pool_mode` from `session` to `transaction`: +```ini +admin_users = pgbouncer +``` -Next up, `max_client_conn` defines how many connections PgBouncer itself will accept, and `default_pool_size` puts a limit on how many Postgres connections will be opened under the hood. (In PgHero the number of connections reported will correspond to `default_pool_size` because it has no knowledge of PgBouncer.) +Mastodon requires a different pooling mode than the default session-based one. Specifically, it needs a transaction-based pooling mode. This means that a PostgreSQL connection is established at the start of a transaction and terminated upon its completion. Therefore, it's essential to change the `pool_mode` setting from `session` to `transaction`: + +```ini +pool_mode = transaction +``` + +Next up, `max_client_conn` defines how many connections PgBouncer itself will accept, and `default_pool_size` puts a limit on how many PostgreSQL connections will be opened under the hood. (In PgHero the number of connections reported will correspond to `default_pool_size` because it has no knowledge of PgBouncer.) The defaults are fine to start, and you can always increase them later: @@ -180,7 +234,7 @@ max_client_conn = 100 default_pool_size = 20 ``` -Don’t forget to reload or restart pgbouncer after making your changes: +Don’t forget to reload or restart PgBouncer after making your changes: ```bash sudo systemctl reload pgbouncer @@ -188,13 +242,13 @@ sudo systemctl reload pgbouncer #### Debugging that it all works {#pgbouncer-debug} -You should be able to connect to PgBouncer just like you would with Postgres: +You should be able to connect to PgBouncer just like you would with PostgreSQL: ```bash psql -p 6432 -U mastodon mastodon_production ``` -And then use your password to log in. +Then use your password to log in. You can also check the PgBouncer logs like so: @@ -212,7 +266,7 @@ PREPARED_STATEMENTS=false Since we’re using transaction-based pooling, we can’t use prepared statements. -Next up, configure Mastodon to use port 6432 (PgBouncer) instead of 5432 (Postgres) and you should be good to go: +Next up, configure Mastodon to use port 6432 (PgBouncer) instead of 5432 (PostgreSQL) and you should be good to go: ```bash DB_HOST=localhost @@ -223,7 +277,7 @@ DB_PORT=6432 ``` {{< hint style="warning" >}} -You cannot use pgBouncer to perform `db:migrate` tasks. But this is easy to work around. If your postgres and pgbouncer are on the same host, it can be as simple as defining `DB_PORT=5432` together with `RAILS_ENV=production` when calling the task, for example: `RAILS_ENV=production DB_PORT=5432 bundle exec rails db:migrate` (you can specify `DB_HOST` too if it’s different, etc) +You cannot use PgBouncer to perform `db:migrate` tasks. But this is easy to work around. If your PostgreSQL and PgBouncer are on the same host, it can be as simple as defining `DB_PORT=5432` together with `RAILS_ENV=production` when calling the task, for example: `RAILS_ENV=production DB_PORT=5432 bundle exec rails db:migrate` (you can specify `DB_HOST` too if it’s different, etc) {{< /hint >}} #### Administering PgBouncer {#pgbouncer-admin} @@ -250,18 +304,87 @@ Then use `\q` to quit. ## Separate Redis for cache {#redis} -Redis is used widely throughout the application, but some uses are more important than others. Home feeds, list feeds, and Sidekiq queues as well as the streaming API are backed by Redis and that’s important data you wouldn’t want to lose (even though the loss can be survived, unlike the loss of the PostgreSQL database - never lose that!). However, Redis is also used for volatile cache. If you are at a stage of scaling up where you are worried if your Redis can handle everything, you can use a different Redis database for the cache. In the environment, you can specify `CACHE_REDIS_URL` or individual parts like `CACHE_REDIS_HOST`, `CACHE_REDIS_PORT` etc. Unspecified parts fallback to the same values as without the cache prefix. +Redis is used widely throughout the application, but some uses are more important than others. Home feeds, list feeds, and Sidekiq queues as well as the streaming API are backed by Redis and that’s important data you wouldn’t want to lose (even though the loss can be survived, unlike the loss of the PostgreSQL database - never lose that!). However, Redis is also used for volatile cache. If you are at a stage of scaling up where you are worried about whether your Redis can handle everything, you can use a different Redis database for the cache. In the environment, you can specify `CACHE_REDIS_URL` or individual parts like `CACHE_REDIS_HOST`, `CACHE_REDIS_PORT` etc. Unspecified parts fallback to the same values as without the cache prefix. -As far as configuring the Redis database goes, basically you can get rid of background saving to disk, since it doesn’t matter if the data gets lost on restart and you can save some disk I/O on that. You can also add a maximum memory limit and a key eviction policy, for that, see this guide: [Using Redis as an LRU cache](https://redis.io/topics/lru-cache) +Additionally, Redis is used for volatile caching. If you're scaling up and concerned about Redis's capacity to handle the load, you can allocate a separate Redis database specifically for caching. To do this, set `CACHE_REDIS_URL` in the environment, or define individual components such as `CACHE_REDIS_HOST`, `CACHE_REDIS_PORT`, etc. + +Unspecified components will default to their values without the cache prefix. + +When configuring the Redis database for caching, it's possible to disable background saving to disk, as data loss on restart is not critical in this context, and this can save some disk I/O. Additionally, consider setting a maximum memory limit and implementing a key eviction policy. For more details on these configurations, refer to this guide:[Using Redis as an LRU cache](https://redis.io/topics/lru-cache) + +## Seperate Redis for Sidekiq {#redis-sidekiq} + +Redis is used in Sidekiq to keep track of its locks and queue. Although in general the performance gain is not that big, some instances may benefit from having a seperate Redis instance for Sidekiq. + +In the environment file, you can specify `SIDEKIQ_REDIS_URL` or individual parts like `SIDEKIQ_REDIS_HOST`, `SIDEKIQ_REDIS_PORT` etc. Unspecified parts fallback to the same values as without the `SIDEKIQ_` prefix. + +Creating a seperate Redis instance for Sidekiq is relatively simple: + +Start by making a copy of the default redis systemd service: +```bash +cp /etc/systemd/system/redis.service /etc/systemd/system/redis-sidekiq.service +``` + +In the `redis-sidekiq.service` file, change the following values: +```bash +ExecStart=/usr/bin/redis-server /etc/redis/redis-sidekiq.conf --supervised systemd --daemonize no +PIDFile=/run/redis/redis-server-sidekiq.pid +ReadWritePaths=-/var/lib/redis-sidekiq +Alias=redis-sidekiq.service +``` + +Make a copy of the Redis configuration file for the new Sidekiq Redis instance + +```bash +cp /etc/redis/redis.conf /etc/redis/redis-sidekiq.conf +``` + +In this `redis-sidekiq.conf` file, change the following values: +```bash +port 6479 +pidfile /var/run/redis/redis-server-sidekiq.pid +logfile /var/log/redis/redis-server-sidekiq.log +dir /var/lib/redis-sidekiq +``` + +Before starting the new Redis instance, create a data directory: + +```bash +mkdir /var/lib/redis-sidekiq +chown redis /var/lib/redis-sidekiq +``` + +Start the new Redis instance: + +```bash +systemctl enable --now redis-sidekiq +``` + +Update your environment, add the following line: + +```bash +SIDEKIQ_REDIS_URL=redis://127.0.0.1:6479/ +``` + +Restart Mastodon to use the new Redis instance, make sure to restart both web and Sidekiq (otherwise, one of them will still be working from the wrong instance): + +```bash +systemctl restart mastodon-web.service +systemctl restart redis-sidekiq.service +``` ## Read-replicas {#read-replicas} -To reduce the load on your Postgresql server, you may wish to setup hot streaming replication (read replica). [See this guide for an example](https://cloud.google.com/community/tutorials/setting-up-postgres-hot-standby). You can make use of the replica in Mastodon in these ways: +To reduce the load on your PostgreSQL server, you may wish to set up hot streaming replication (read replica). [See this guide for an example](https://cloud.google.com/community/tutorials/setting-up-postgres-hot-standby). You can make use of the replica in Mastodon in these ways: -* The streaming API server does not issue writes at all, so you can connect it straight to the replica. But it’s not querying the database very often anyway so the impact of this is little. -* Use the Makara driver in the web and sidekiq processes, so that writes go to the master database, while reads go to the replica. Let’s talk about that. +* The streaming API server does not issue writes at all, so you can connect it straight to the replica (it is not querying the database very often anyway, so the impact of this is small). +* Use the Makara driver in the web and Sidekiq processes, so that writes go to the master database, while reads go to the replica. Let’s talk about that. -You will have to edit the `config/database.yml` file and replace the `production` section as follows: +{{< hint style="warning" >}} +Read replicas are currently not supported for the Sidekiq processes, and using them will lead to failing jobs and data loss. +{{< /hint >}} + +You will have to use a separate `config/database.yml` file for the web processes and edit it to replace the `production` section as follows: ```yaml production: @@ -279,9 +402,25 @@ production: url: postgresql://db_user:db_password@db_host:db_port/db_name ``` -Make sure the URLs point to wherever your PostgreSQL servers are. You can add multiple replicas. You could have a locally installed pgBouncer with configuration to connect to two different servers based on database name, e.g. “mastodon” going to master, “mastodon_replica” going to the replica, so in the file above both URLs would point to the local pgBouncer with the same user, password, host and port, but different database name. There are many possibilities how this could be setup! For more information on Makara, [see their documentation](https://github.com/taskrabbit/makara#databaseyml). +Make sure the URLs point to wherever your PostgreSQL servers are. You can add multiple replicas. You could have a locally installed PgBouncer with a configuration to connect to two different servers based on the database name, e.g. “mastodon” going to the primary, “mastodon_replica” going to the replica, so in the file above both URLs would point to the local PgBouncer with the same user, password, host and port, but different database name. There are many possibilities how this could be set up! For more information on Makara, [see their documentation](https://github.com/taskrabbit/makara#databaseyml). {{< hint style="warning" >}} -Sidekiq cannot reliably use read-replicas because even the tiniest replication lag leads to failing jobs due to queued up records not being found. +Make sure the sidekiq processes run with the stock `config/database.yml` to avoid failing jobs and data loss! {{< /hint >}} +## Using a web load balancer + +Cloud providers like DigitalOcean, AWS, Hetzner, etc., offer virtual load balancing solutions that distribute network traffic across multiple servers, but provide a single public IP address. + +Scaling your deployment to provision multiple web/Puma servers behind one of these virtual load balancers can help provide more consistent performance by reducing the risk that a single server may become overwhelmed by user traffic, and decrease downtime when performing maintenance or upgrades. You should consult your provider documentation on how to setup and configure a load balancer, but consider that you need to configure your load balancer to monitor the health of the backend web/Puma nodes, otherwise you may send traffic to a service that is not responsive. + +The following endpoints are available to monitor for this purpose: + +- **Web/Puma:** `/health` +- **Streaming API:** `/api/v1/streaming/health` + +These endpoints should both return an HTTP status code of 200, and the text `OK` as a result. + +{{< hint style="info" >}} +You can also use these endpoints for health checks with a third-party monitoring/alerting utility. +{{< /hint >}} \ No newline at end of file diff --git a/content/en/admin/tootctl.md b/content/en/admin/tootctl.md index 73f20db1..3ae1285e 100644 --- a/content/en/admin/tootctl.md +++ b/content/en/admin/tootctl.md @@ -22,7 +22,7 @@ RAILS_ENV=production bin/tootctl help ## Base CLI -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/cli.rb" caption="lib/cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/base.rb" caption="lib/mastodon/cli/base.rb" >}} --- @@ -32,7 +32,7 @@ RAILS_ENV=production bin/tootctl help Erase this server from the federation by broadcasting account Delete activities to all known other servers. This allows a "clean exit" from running a Mastodon server, as it leaves next to no cache behind on other servers. This command is always interactive and requires confirmation twice. -No local data is actually deleted, because emptying the database or deleting the entire VPS is faster. If you run this command then continue to operate the instance anyway, then there will be a state mismatch that might lead to glitches and issues with federation. +No local data is actually deleted because emptying the database or deleting the entire VPS is faster. If you run this command and then continue to operate the instance anyway, then there will be a state mismatch that might lead to glitches and issues with federation. {{< hint style="danger" >}} **Make sure you know exactly what you are doing before running this command.** This operation is NOT reversible, and it can take a long time. The server will be in a BROKEN STATE after this command finishes. A running Sidekiq process is required, so do not shut down the server until the queues are fully cleared. @@ -61,7 +61,7 @@ Show the version of the currently running Mastodon instance. ## Accounts CLI {#accounts} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/accounts_cli.rb" caption="lib/mastodon/accounts_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/accounts.rb" caption="lib/mastodon/cli/accounts.rb" >}} --- @@ -86,7 +86,7 @@ Generate and broadcast new RSA keys, as part of security maintenance. ### `tootctl accounts create` {#accounts-create} -Create a new user account with given `USERNAME` and provided `--email`. +Create a new user account with the given `USERNAME` and provided `--email`. `USERNAME` : Local username for the new account. {{}} @@ -146,7 +146,7 @@ Modify a user account's role, email, active status, approval mode, or 2FA requir : Approve `USERNAME`'s account, if you are/were in approval mode. `--disable-2fa` -: Remove additional factors and allow login with password. +: Remove additional factors and allow login with a password. `--reset-password` : Resets the password of the given account. @@ -165,7 +165,7 @@ Modify a user account's role, email, active status, approval mode, or 2FA requir ### `tootctl accounts delete` {#accounts-delete} -Delete a user account with given USERNAME. +Delete a user account with the given USERNAME. `USERNAME` : Local username for the new account. {{}} @@ -179,7 +179,7 @@ Delete a user account with given USERNAME. ### `tootctl accounts backup` {#accounts-backup} -Request a backup for a user account with given USERNAME. The backup will be created in Sidekiq asynchronously, and the user will receive an email with a link to it once it's done. +Request a backup for a user account with the given USERNAME. The backup will be created in Sidekiq asynchronously, and the user will receive an email with a link to it once it's done. `USERNAME` : Local username for the new account. {{}} @@ -207,7 +207,7 @@ Remove remote accounts that no longer exist. Queries every single remote account **Version history:**\ 2.6.0 - added\ 2.8.0 - add `--dry-run`\ -3.5.0 - add ability to pass specific domains +3.5.0 - add the ability to pass specific domains --- @@ -230,7 +230,7 @@ Refetch remote user data and files for one or multiple accounts. : The number of workers to use for this task. Defaults to N=5. `--verbose` -: Print additional information while task is processing. +: Print additional information while a task is processing. `--dry-run` : Print expected results only, without performing any actions. @@ -331,7 +331,7 @@ Approve new registrations when instance is in approval mode. : Local username. `--number N` -: Approve the N most recent registrations. +: Approve the N earliest pending registrations. `--all` : Approve all pending registrations. @@ -345,7 +345,7 @@ Approve new registrations when instance is in approval mode. ## Cache CLI {#cache} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/cache_cli.rb" caption="lib/mastodon/cache_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/cache.rb" caption="lib/mastodon/cli/cache.rb" >}} --- @@ -384,7 +384,7 @@ Update hard-cached counters of TYPE by counting referenced records from scratch. ## Domains CLI {#domains} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/domains_cli.rb" caption="lib/mastodon/domains_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/domains.rb" caption="lib/mastodon/cli/domains.rb" >}} --- @@ -450,7 +450,7 @@ Crawl the known fediverse by using Mastodon REST API endpoints that expose all k ## Email domain blocks CLI {#email-domain-blocks} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/email_domain_blocks_cli.rb" caption="lib/mastodon/email_domain_blocks_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/email_domain_blocks.rb" caption="lib/mastodon/cli/email_domain_blocks.rb" >}} --- @@ -500,7 +500,7 @@ Remove entries from the e-mail domain blocklist. ## Emoji CLI {#emoji} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/emoji_cli.rb" caption="lib/mastodon/emoji_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/emoji.rb" caption="lib/mastodon/cli/emoji.rb" >}} --- @@ -557,7 +557,7 @@ Imports custom emoji from a .tar.gz archive at a given path. The archive should ### `tootctl emoji purge` {#emoji-purge} -Remove all custom emoji. +Remove all custom emojis. `--remote-only` : If provided, remove only from remote domains. @@ -572,7 +572,7 @@ Remove all custom emoji. ## Feeds CLI {#feeds} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/feeds_cli.rb" caption="lib/mastodon/feeds_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/feeds.rb" caption="lib/mastodon/cli/feeds.rb" >}} --- @@ -617,7 +617,7 @@ Remove all home and list feeds from Redis. ## Maintenance CLI {#maintenance} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/maintenance_cli.rb" caption="lib/mastodon/maintenance_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/maintenance.rb" caption="lib/mastodon/cli/maintenance.rb" >}} --- @@ -636,7 +636,7 @@ Fix corrupted database indexes that may have been caused due to changing collati ## Media CLI {#media} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/media_cli.rb" caption="lib/mastodon/media_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/media.rb" caption="lib/mastodon/cli/media.rb" >}} --- @@ -644,14 +644,23 @@ Fix corrupted database indexes that may have been caused due to changing collati ### `tootctl media remove` {#media-remove} -Remove locally cached copies of media attachments from other servers. +Removes locally cached copies of media attachments, avatars or profile headers from other servers. By default, only media attachments are removed. `--days N` -: How old media attachments have to be before they are removed. Defaults to 7. +: How old media attachments have to be before they are removed. In case of avatars and headers, how old the last webfinger request and update to the user has to be before they are removed. Defaults to 7. `--concurrency N` : The number of workers to use for this task. Defaults to N=5. +`--prune-profiles` +: Instead of media attachments, remove locally cached copies of avatars and headers from other servers. Cannot be combined with `--remove-headers`. + +`--remove-headers` +: Instead of media attachments, remove locally cached copies of headers from other servers. Cannot be combined with `--prune-profiles`. + +`--include-follows` +: Override the default behavior of `--prune-profiles` and `--remove-headers` to remove locally cached copies of avatars (and headers) from other servers, irrespective of follow status (by default, they are only removed from accounts that are not followed by or following anyone locally). Can only be used with `--prune-profiles` or `--remove-headers`. + `--verbose` : Print additional information while task is processing. @@ -660,7 +669,8 @@ Remove locally cached copies of media attachments from other servers. **Version history:**\ 2.5.0 - added\ -2.6.2 - show freed disk space +2.6.2 - show freed disk space\ +4.1.0 - added --prune-profiles, --remove-headers, and --include-follows. --- @@ -693,7 +703,7 @@ Scans for files that do not belong to existing media attachments, and remove the ### `tootctl media refresh` {#media-refresh} -Refetch remote media attachments from other servers. You must specify the source of media attachments with either `--status`, `--account`, or `--domain`. If an attachment already exists in the database, it will not be overwritten unless you use `--force`. +Refetch remote media attachments from other servers. You must specify the source of media attachments with either `--status`, `--account`, `--domain`, or `--days`. If an attachment already exists in the database, it will not be overwritten unless you use `--force`. `--account ACCT` : String `username@domain` handle of the account @@ -704,6 +714,9 @@ Refetch remote media attachments from other servers. You must specify the source `--status ID` : Local numeric ID of the status in the database. +`--days N` +: The number of days to limit this task to. + `--concurrency N` : The number of workers to use for this task. Defaults to 5. @@ -718,7 +731,8 @@ Refetch remote media attachments from other servers. You must specify the source **Version history:**\ 3.0.0 - added\ -3.0.1 - add `--force` and skip already downloaded attachments by default +3.0.1 - add `--force` and skip already downloaded attachments by default\ +4.0.0 - add `--days` --- @@ -748,7 +762,7 @@ Prompts for a media URL, then looks up the status where the media is displayed. ## Preview Cards CLI {#preview_cards} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/preview_cards_cli.rb" caption="lib/mastodon/preview_cards_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/preview_cards.rb" caption="lib/mastodon/cli/preview_cards.rb" >}} --- @@ -782,7 +796,7 @@ Remove local thumbnails for preview cards. ## Search CLI {#search} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/search_cli.rb" caption="lib/mastodon/search_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/search.rb" caption="lib/mastodon/cli/search.rb" >}} --- @@ -790,10 +804,10 @@ Remove local thumbnails for preview cards. ### `tootctl search deploy` {#search-deploy} -Create or update an ElasticSearch index and populate it. If ElasticSearch is empty, this command will create the necessary indices and then import data from the database into those indices. This command will also upgrade indices if the underlying schema has been changed since the last run. +Create or update an Elasticsearch index and populate it. If Elasticsearch is empty, this command will create the necessary indices and then import data from the database into those indices. This command will also upgrade indices if the underlying schema has been changed since the last run. `--batch-size` -: Defaults to 1000. A lower batch size can make ElasticSearch process records more quickly. +: Defaults to 100. A higher batch size can make Elasticsearch process records more quickly, with less load on the PostgreSQL database, but can increase memory pressure on the Elasticsearch nodes during indexing. `--only INDEX` : Specify an index name [`accounts`, `tags`, `statuses`] to create or update only that index. @@ -813,7 +827,7 @@ Create or update an ElasticSearch index and populate it. If ElasticSearch is emp ## Settings CLI {#settings} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/settings_cli.rb" caption="lib/mastodon/settings_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/settings.rb" caption="lib/mastodon/cli/settings.rb" >}} --- @@ -857,7 +871,7 @@ Set registration to require approval. ## Statuses CLI {#statuses} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/statuses_cli.rb" caption="lib/mastodon/statuses_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/statuses.rb" caption="lib/mastodon/cli/statuses.rb" >}} --- @@ -886,7 +900,7 @@ This is a computationally heavy procedure that creates extra database indices be ## Upgrade CLI {#upgrade} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/lib/mastodon/upgrade_cli.rb" caption="lib/mastodon/upgrade_cli.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/cli/upgrade.rb" caption="lib/mastodon/cli/upgrade.rb" >}} --- @@ -903,4 +917,4 @@ Upgrade the storage schema to store all non-local media resources in a top-level : Print expected results only, without performing any actions. **Version history:**\ -3.1.4 - added \ No newline at end of file +3.1.4 - added diff --git a/content/en/admin/troubleshooting.md b/content/en/admin/troubleshooting.md index 3099d97e..6ff6cb31 100644 --- a/content/en/admin/troubleshooting.md +++ b/content/en/admin/troubleshooting.md @@ -11,10 +11,21 @@ menu: All error messages with stack traces are written to the system log. When using systemd, the logs of each systemd service can be browsed with `journalctl -u mastodon-web` (substitute with the correct service name). When using Docker, it’s similar: `docker logs mastodon_web_1` (substitute with the correct container name). -Specific details of server-side errors are _never_ displayed to the public, as they can reveal what your setup looks like internally and give attackers clues how to get in, or how to abuse the system more efficiently. +Specific details of server-side errors are _never_ displayed to the public, as they can reveal what your setup looks like internally and give attackers clues on how to get in, or how to abuse the system more efficiently. Each response from Mastodon’s web server carries a header with a unique request ID, which is also reflected in the logs. By inspecting the headers of the error page, you can easily find the corresponding stack trace in the log. +## **I'm not seeing much in my logs. How do I enable additional logging/debugging information?** + +By default your logs will show `info` level logging. To see more debugging messages, you can your `.env.production` file to increase the level, for the relevant service: + +- **Web/Sidekiq:** Set the value of `RAILS_LOG_LEVEL` to `debug` and then restart the service that you're attempting to troubleshoot. +- **Streaming:** Set the value of `LOG_LEVEL` to `silly` and then restart the service that you're attempting to troubleshoot. + +More information on other logging levels for these option can be found on the [Configuring your environment](https://docs.joinmastodon.org/admin/config) page. + +The `debug` or `silly` levels can be very verbose and you should take care to change the log level back to a lower level, once you have completed your troubleshooting. + ## **After an upgrade to a newer version, some pages look weird, like they have unstyled elements. Why?** Check that you have run `RAILS_ENV=production bin/rails assets:precompile` after the upgrade, and restarted Mastodon’s web process, because it looks like it’s serving outdated stylesheets and scripts. It’s also possible that the precompilation fails due to a lack of RAM, as webpack is unfortunately extremely memory-hungry. If that is the case, make sure you have some swap space assigned. Alternatively, it’s possible to precompile the assets on a different machine, then copy over the `public/packs` directory. @@ -29,4 +40,12 @@ Check that you are specifying the correct environment with `RAILS_ENV=production ## **I encountered a compilation error while executing `RAILS_ENV=production bundle exec rails assets:precompile`, but no more information is given. How to fix it?** -Usually it's because your server ran out of memory while compiling assets. Use a swapfile or increase the swap space to increase the memory capacity. Run `RAILS_ENV=production bundle exec rake tmp:cache:clear` to clear cache, then execute `RAILS_ENV=production bundle exec rails assets:precompile` to compile again. Make sure you clear the cache after a compilation error, or it will show "Everything's OK" but leave the assets unchanged. +Usually, it's because your server ran out of memory while compiling assets. Use a swapfile or increase the swap space to increase the memory capacity. Run `RAILS_ENV=production bundle exec rake tmp:cache:clear` to clear cache, then execute `RAILS_ENV=production bundle exec rails assets:precompile` to compile again. Make sure you clear the cache after a compilation error, or it will show "Everything's OK" but leave the assets unchanged. + +## **I am getting this error: `Read-only file system @ dir_s_mkdir`. Why?** + +By default, Mastodon makes use of [systemd's sandboxing capabilities](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Sandboxing) in a way that disallows writing outside of `/home/mastodon`. If Mastodon is installed elsewhere, you may need to allow `mastodon-sidekiq` and `mastodon-web` to write to a custom directory: +1. Add parameter `ReadWritePaths` to files `/etc/systemd/system/mastodon-sidekiq.service` and `/etc/systemd/system/mastodon-web.service`. Example - `ReadWritePaths=/example/mastodon/live`. +2. run `systemctl stop mastodon-sidekiq mastodon-web` +3. run `systemctl daemon-reload` +4. run `systemctl start mastodon-sidekiq mastodon-web` diff --git a/content/en/admin/troubleshooting/index-corruption.md b/content/en/admin/troubleshooting/index-corruption.md index 78a2ef61..b0fa0f62 100644 --- a/content/en/admin/troubleshooting/index-corruption.md +++ b/content/en/admin/troubleshooting/index-corruption.md @@ -15,17 +15,17 @@ Textual values in the database, such as usernames, or status identifiers, are co When setting up a database, Mastodon will use the database server's default locale settings, including the default collation rules, which often is defined by the operating system's settings. Unfortunately, in late 2018, a `glibc` update changed the collation rules for many locales, which means databases using an affected locale would now order textual values differently. -Since the database indexes are algorithmic structures which rely on the ordering of the values they are indexing, some of them would become inconsistent. +Since the database indexes are algorithmic structures that rely on the ordering of the values they are indexing, some of them would become inconsistent. More information: https://wiki.postgresql.org/wiki/Locale_data_changes https://postgresql.verite.pro/blog/2018/08/27/glibc-upgrade.html ## Am I affected by this issue? {#am-i-affected} If your database is not using `C` or `POSIX` for its collation setting (which you can check with `SELECT datcollate FROM pg_database WHERE datname = current_database();`), -your indexes might be inconsistent, if you ever ran with a version of glibc prior to 2.28 and did not immediately reindex your databases after updating to glibc 2.28 or newer. +your indexes might be inconsistent if you ever ran with a version of glibc prior to 2.28 and did not immediately reindex your databases after updating to glibc 2.28 or newer. {{< hint style="info" >}} -You may have found this page because of **PgHero** warnings about "Duplicate Indexes". While such warnings can sometimes be indicative of an issue in deploying or updating Mastodon, **they are not related to database index corruption and not indicative of any functional issue with your database**. +You may have found this page because of PgHero warnings about "Duplicate Indexes". While such warnings can sometimes be indicative of an issue in deploying or updating Mastodon, **they are not related to database index corruption and are not indicative of any functional issue with your database**. {{< /hint >}} You can check whether your indexes are consistent using [PostgreSQL's `amcheck` module](https://www.postgresql.org/docs/10/amcheck.html): as the database server's super user, connect to your Mastodon database and issue the following (this may take a while): @@ -79,14 +79,14 @@ If this succeeds, without returning an error, your database should be consistent ## Fixing the issue {#fixing} -Unless you take action, if you are affected, your database could get more and more inconsistent as the time pass. Therefore, it is important to fix it as soon as possible. +Unless you take action, if you are affected, your database could get more and more inconsistent as time passes. Therefore, it is important to fix it as soon as possible. Mastodon 3.2.2 and later come with a semi-interactive script to fix those corruptions as best as possible. If you're on an earlier version, update to 3.2.2 first. It is possible that running the database migrations to 3.2.2 will fail because of those very corruptions, but the database should then be brought to a state that the maintenance tool bundled with Mastodon 3.2.2 can then recover from. Before attempting to fix your database, **stop Mastodon and make a backup of your database**. Then, with **Mastodon still stopped**, run the maintenance script: ``` -RAILS_ENV=production tootctl maintenance fix-duplicates +RAILS_ENV=production bin/tootctl maintenance fix-duplicates ``` The script will walk through the database to automatically find duplicates and fix them. In some cases, those operations are destructive. In the most destructive cases, you will be asked to choose which record to keep and which records to discard. In all cases, walking through the whole database in search of duplicates is an extremely long operation. diff --git a/content/en/admin/upgrading.md b/content/en/admin/upgrading.md index 2497f1ba..a47681dc 100644 --- a/content/en/admin/upgrading.md +++ b/content/en/admin/upgrading.md @@ -7,9 +7,19 @@ menu: --- {{< hint style="info" >}} -When a new version of Mastodon comes out, it appears on the [GitHub releases page](https://github.com/mastodon/mastodon/releases). Please mind that running unreleased code from the `master` branch, while possible, is not recommended. +When a new version of Mastodon comes out, it appears on the [GitHub releases page](https://github.com/mastodon/mastodon/releases). Please mind that running unreleased code from the `main` branch, while possible, is not recommended. {{< /hint >}} +### Automatic update verification {#automated_checks} + +Since v4.2.0, Mastodon will automatically check for available updates and notify the users of your server that have the `DevOps` permission. + +This happens by fetching `https://api.joinmastodon.org/update-check?version=` in the background every 30 minutes. `current_version` omits the build metadata (everything after the first `+`, if there is one, in the version string). For instance, if your version is `4.3.0-beta2+my-fork`, Mastodon will query `https://api.joinmastodon.org/update-check?version=4.3.0-beta2`. + +You can change which URL Mastodon queries by setting the `UPDATE_CHECK_URL` environment variable. You can also completely disable this behavior by setting this environment variable to an empty string, although we strongly recommend against doing that unless you are keeping up with Mastodon updates in another way, as Mastodon occasionally releases critical security updates that must be applied in a timely fashion. + +### Upgrade steps + Mastodon releases correspond to git tags. Before attempting an upgrade, look up the desired release on the [GitHub releases page](https://github.com/mastodon/mastodon/releases). The page will contain a **changelog** describing everything you need to know about what's different, as well as **specific upgrade instructions**. To begin, switch to the `mastodon` user: @@ -24,7 +34,7 @@ And navigate to the Mastodon root directory: cd /home/mastodon/live ``` -Download the releases’s code, assuming that the version is called `v3.1.2`: +Download the release’s code, assuming that the version is called `v3.1.2`: ```bash git fetch --tags @@ -59,14 +69,14 @@ systemctl reload mastodon-web The `reload` operation is a zero-downtime restart, also called "phased restart". As such, Mastodon upgrades usually do not require any advance notice to users about planned downtime. In rare cases, you can use the `restart` operation instead, but there will be a (short) felt interruption of service for your users. {{< /hint >}} -Rarely, the **streaming API** server is also updated and requires a restart: +The **streaming API** server is also updated and requires a restart, doing so will result in all connected clients being disconnected, which can increase the load on your server: ```bash systemctl restart mastodon-streaming ``` {{< hint style="danger" >}} -The streaming API server is updated very rarely, and in most releases, does *not* require a restart. Restarting the streaming API leads to an increased load on your server as disconnected clients attempt to reconnect or poll the REST API instead, so avoid it whenever you can. +Restarting the streaming API leads to an increased load on your server as disconnected clients attempt to reconnect or poll the REST API instead, so avoid it whenever you can. {{< /hint >}} {{< hint style="success" >}} diff --git a/content/en/api/guidelines.md b/content/en/api/guidelines.md index c39cec82..e5e43b6c 100644 --- a/content/en/api/guidelines.md +++ b/content/en/api/guidelines.md @@ -9,7 +9,7 @@ menu: ## Login {#login} -**The user must be able to login to any Mastodon server from the app**. This means you must ask for the server's domain and use the app registrations API to dynamically obtain OAuth2 credentials. +**The user must be able to log in to any Mastodon server from the app**. This means you must ask for the server's domain and use the app registrations API to dynamically obtain OAuth2 credentials. {{< page-ref page="client/authorized" >}} @@ -19,11 +19,11 @@ menu: ## Usernames {#username} -**Decentralization must be transparent to the user**. It should be possible to see that a given user is from another server, for example, by displaying their `acct` somewhere. Note that `acct` is equal to `username` for local users, and equal to `username@domain` for remote users. +**Decentralization must be transparent to the user**. It should be possible to see that a given user is from another server, for example, by displaying their `acct` somewhere. Note that `acct` is equal to `username` for local users and equal to `username@domain` for remote users. ## Handling and sorting IDs {#id} -Vanilla Mastodon entity IDs are generated as integers and cast to string. However, this does not mean that IDs _are_ integers, nor should they be cast to integer! Doing so can lead to broken client apps due to integer overflow, so **always treat IDs as strings.** +Vanilla Mastodon entity IDs are generated as integers and cast to string. However, this does not mean that IDs _are_ integers, nor should they be cast to integers! Doing so can lead to broken client apps due to integer overflow, so **always treat IDs as strings.** With that said, because IDs are string representations of numbers, they can still be sorted chronologically very easily by doing the following: @@ -32,7 +32,27 @@ With that said, because IDs are string representations of numbers, they can stil ## Paginating through API responses {#pagination} -Many API methods allow you to paginate for more information, using parameters such as `limit`, `max_id`, `min_id`, and `since_id`. However, some of these API methods operate on entity IDs that are not publicly exposed in the API response, and are only known to the backend and the database. (This is usually the case for entities that reference other entities, such as Follow entities which reference Accounts, or Favourite entities which reference Statuses, etc.) +Many API methods allow you to paginate for more information, using parameters such as `limit`, `max_id`, `min_id`, and `since_id`. + +limit +: The maximum number of results to return. Usually, there is a default limit and a maximum limit; these will vary according to the API method. + +max_id +: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results. + +since_id +: String. All results returned will be greater than this ID. In effect, sets a lower bound on results. + +min_id +: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward. (Available since v2.6.0.) + +For example, we might fetch `https://mastodon.example/api/v1/accounts/1/statuses` with certain parameters, and we will get the following results in the following cases: + +- Setting `?max_id=1` will return no statuses since there are no statuses with an ID earlier than `1`. +- Setting `?since_id=1` will return the latest statuses since there have been many statuses since `1`. +- Setting `?min_id=1` will return the oldest statuses, as `min_id` sets the cursor. + +Some API methods operate on entity IDs that are not publicly exposed in the API response and are only known to the backend and the database. (This is usually the case for entities that reference other entities, such as Follow entities which reference Accounts, or Favourite entities which reference Statuses, etc.) To get around this, Mastodon may return links to a "prev" and "next" page. These links are made available via the HTTP `Link` header on the response. Consider the following fictitious API call: @@ -40,7 +60,7 @@ To get around this, Mastodon may return links to a "prev" and "next" page. These GET https://mastodon.example/api/v1/endpoint HTTP/1.1 Authorization: Bearer token -Link: ; rel="next", ; rel="prev" +Link: ; rel="next", ; rel="prev" [ { // some Entity @@ -71,7 +91,7 @@ Plain text is not available for content from remote servers, and plain text synt ### Mentions, hashtags, and custom emoji {#tags} -Mentions and hashtags are `` tags. Custom emoji remain in their plain text shortcode form. To give those entities their semantic meaning and add special handling, such as opening a mentioned profile within your app instead of as a web page, metadata is included with the [Status]({{< relref "entities/Status" >}}), which can be matched to a particular tag. +Mentions and hashtags are `` tags. Custom emojis remain in their plain text shortcode form. To give those entities their semantic meaning and add special handling, such as opening a mentioned profile within your app instead of as a web page, metadata is included with the [Status]({{< relref "entities/Status" >}}), which can be matched to a particular tag. {{< page-relref ref="entities/Status" caption="Status entity" >}} @@ -83,7 +103,7 @@ Mentions and hashtags are `` tags. Custom emoji remain in their plain text sh ### Link shortening {#links} -Links in Mastodon are not shortened using URL shorteners, and the usage of URL shorteners is heavily discouraged. URLs in text always count for 23 characters, and are intended to be shortened visually. For that purpose, a link is marked up like this: +Links in Mastodon are not shortened using URL shorteners, and the usage of URL shorteners is heavily discouraged. URLs in text always count for 23 characters and are intended to be shortened visually. For that purpose, a link is marked up like this: ```html @@ -93,7 +113,7 @@ Links in Mastodon are not shortened using URL shorteners, and the usage of URL s ``` -The spans with the `invisible` class can be hidden. The middle span is intended to remain visible. It may have no class if the URL is not very long; otherwise it will have an `ellipsis` class. No ellipsis (`…`) character is inserted in the markup; instead, you are expected to insert it yourself if you need it in your app. +The spans with the `invisible` class can be hidden. The middle span is intended to remain visible. It may have no class if the URL is not very long; otherwise, it will have an `ellipsis` class. No ellipsis (`…`) character is inserted in the markup; instead, you are expected to insert it yourself if you need it in your app. ## Filters {#filters} @@ -101,7 +121,7 @@ The spans with the `invisible` class can be hidden. The middle span is intended If a filter applies to a Status, a corresponding FilterResult will be included in the `filtered` attribute. Clients should check this attribute for any matches and use them to apply the intended filter action. -However, client implementations may still want to perform their own rule matching client-side, as this would allow retroactively apply filter changes without re-fetching posts from the server. When doing so, they should take care to not ignore `filtered` entries for which there are other attributes than `keyword_matches`, so as to handle extensions of the filtering system (e.g. `status_matches`). +However, client implementations may still want to perform their own rule matching client-side, as this would allow retroactively applying filter changes without re-fetching posts from the server. When doing so, they should take care to not ignore `filtered` entries for which there are other attributes than `keyword_matches`, so as to handle extensions of the filtering system (e.g. `status_matches`). Matched filters need to be filtered based on context (`home`, `notifications`, `public`, `thread` or `profile`) and expiration date. @@ -118,17 +138,17 @@ Expired filters are not deleted by the server. They should no longer be applied, If `whole_word` is true, the client app should do the following: * Define ‘word constituent characters’ for your app. In the official implementation, it’s `[A-Za-z0-9_]` in JavaScript, and `[[:word:]]` in Ruby. Ruby uses the POSIX character class (Letter | Mark | Decimal_Number | Connector_Punctuation). -* If the phrase starts with a word character, and if the previous character before matched range is a word character, its matched range should be treated to not match. -* If the phrase ends with a word character, and if the next character after matched range is a word character, its matched range should be treated to not match. +* If the phrase starts with a word character, and if the previous character before the matched range is a word character, its matched range should be treated to not match. +* If the phrase ends with a word character, and if the next character after the matched range is a word character, its matched range should be treated to not match. Please check `app/javascript/mastodon/selectors/index.js` and `app/lib/feed_manager.rb` in the Mastodon source code for more details. -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/javascript/mastodon/selectors/index.js" caption="app/javascript/mastodon/selectors/index.js" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/javascript/mastodon/selectors/index.js" caption="app/javascript/mastodon/selectors/index.js" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/lib/feed_manager.rb" caption="app/lib/feed_manager.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/lib/feed_manager.rb" caption="app/lib/feed_manager.rb" >}} ## Focal points for cropping media thumbnails {#focal-points} -Server-side preview images are never cropped, to support a variety of apps and user interfaces. Therefore, the cropping must be done by those apps. To crop intelligently, focal points can be used to ensure a certain section of the image is always within the cropped viewport. See this [guide on how focal points are defined](https://github.com/jonom/jquery-focuspoint#1-calculate-your-images-focus-point). In summary, floating points range from -1.0 to 1.0, left-to-right or bottom-to-top. (0,0) is the center of the image. (0.5, 0.5) would be in the center of the upper-right quadrant. (-0.5, -0.5) would be in the center of the lower-left quadrant. For reference, thumbnails in the Mastodon frontend are most commonly 16:9. +Server-side preview images are never cropped, to support a variety of apps and user interfaces. Therefore, the cropping must be done by those apps. To crop intelligently, focal points can be used to ensure a certain section of the image is always within the cropped viewport. See this [guide on how focal points are defined](https://github.com/jonom/jquery-focuspoint#1-calculate-your-images-focus-point). In summary, floating points range from -1.0 to 1.0, left-to-right or bottom-to-top. (0,0) is the center of the image. (0.5, 0.5) would be in the center of the upper-right quadrant. (-0.5, -0.5) would be in the center of the lower-left quadrant. For reference, thumbnails in the Mastodon front end are most commonly 16:9. {{< figure src="assets/focal-points.jpg" caption="A demonstration of various focal points and their coordinates." >}} diff --git a/content/en/api/oauth-scopes.md b/content/en/api/oauth-scopes.md index b42beddf..93512f28 100644 --- a/content/en/api/oauth-scopes.md +++ b/content/en/api/oauth-scopes.md @@ -17,9 +17,9 @@ Multiple scopes can be requested at the same time: During app creation with the Mind the `scope` vs `scopes` difference. This is because `scope` is a standard OAuth parameter name, so it is used in the OAuth methods. Mastodon’s own REST API uses the more appropriate `scopes`. {{< /hint >}} -If you do not specify a `scope` in your authorization request, or a `scopes` in your app creation request, the resulting access token / app will default to `read` access. +If you do not specify a `scope` in your authorization request, or a `scopes` in your app creation request, the resulting access token/app will default to `read` access. -The set of scopes saved during app creation must include all the scopes that you will request in the authorization request, otherwise authorization will fail. +The set of scopes saved during app creation must include all the scopes that you will request in the authorization request, otherwise, authorization will fail. ### Version history {#versions} diff --git a/content/en/client/authorized.md b/content/en/client/authorized.md index cbe9ef56..78226299 100644 --- a/content/en/client/authorized.md +++ b/content/en/client/authorized.md @@ -9,7 +9,7 @@ menu: ## Scopes explained {#scopes} -When we registered our app and when we will authorize our user, we need to define what exactly our generated token will have permission to do. This is done through the use of OAuth scopes. Each API method has an associated scope, and can only be called if the token being used for authorization has been generated with the corresponding scope. +When we register our app and when we authorize our user, we need to define what exactly our generated token will have permission to do. This is done through the use of OAuth scopes. Each API method has an associated scope, and can only be called if the token being used for authorization has been generated with the corresponding scope. Scopes must be a subset. When we created our app, we specified `read write push` -- we could request all available scopes by specifying `read write push`, but it is a better idea to only request what your app will actually need through granular scopes. See [OAuth Scopes]({{< relref "api/oauth-scopes" >}}) for a full list of scopes. Each API method's documentation will also specify the OAuth access level and scope required to call it. @@ -109,7 +109,7 @@ With our OAuth token for the authorized user, we can now perform any action as t * See [/api/v2/search]({{< relref "methods/search#v2" >}}) for querying resources. * See [/api/v1/suggestions]({{< relref "methods/suggestions" >}}) for suggested accounts to follow. -### Use safety features {#safety} +### User safety features {#safety} * See [/api/v1/filters]({{< relref "methods/filters" >}}) for managing filtered keywords. * See [/api/v1/domain_blocks]({{< relref "methods/domain_blocks" >}}) for managing blocked domains. diff --git a/content/en/client/intro.md b/content/en/client/intro.md index 2f14aad2..6f1a85ab 100644 --- a/content/en/client/intro.md +++ b/content/en/client/intro.md @@ -9,7 +9,7 @@ menu: ## An introduction to REST {#rest} -Mastodon provides access to its data over a REST API. REST stands for REpresentational State Transfer, but for our purposes, just think of it as sending and receiving information about various resources based on the request. The Mastodon REST API uses HTTP for its requests, and JSON for its payloads. +Mastodon provides access to its data over a REST API. REST stands for REpresentational State Transfer, but for our purposes, just think of it as sending and receiving information about various resources based on the request. The Mastodon REST API uses HTTP for its requests and JSON for its payloads. ## Understanding HTTP requests and responses {#http} @@ -64,7 +64,7 @@ curl -X POST \ ### JSON {#json} -*JavaScript Object Notation* as defined in ECMA-404. Quick one page overview: https://www.json.org/ +*JavaScript Object Notation* as defined in ECMA-404. Quick one-page overview: https://www.json.org/ Similar to sending form data, but with an additional header to specify that the data is in JSON format. To send a JSON request with cURL, specify the JSON content type with a header, then send the JSON data as form data: @@ -113,7 +113,7 @@ As JSON, hashes are formatted like so: { "source": { "privacy": "public", - "language", "en" + "language": "en" } } ``` diff --git a/content/en/client/libraries.md b/content/en/client/libraries.md index 42f36c18..9f5bd12c 100644 --- a/content/en/client/libraries.md +++ b/content/en/client/libraries.md @@ -1,18 +1,22 @@ --- title: Libraries and implementations -description: Interface with the Mastodon API in the programming language of your choice. +description: Code, libraries and SDKs for the Mastodon API in a range of programming languages. menu: docs: weight: 60 parent: client --- -## Apex (Salesforce) {#apex-salesforce} +Thank you to our awesome developer community, for supporting the project with a wide range of implementations of the API. If you have built a library or SDK for the Mastodon API, [let us know](https://github.com/mastodon/mastodon/discussions) about it, and it may be included below in a future update. -* [apex-mastodon](https://github.com/tzmfreedom/apex-mastodon) +Remember to check how recently the library was updated, and whether it includes the API features you may want to use. +## Arduino / ESP32 / IoT {#arduino-iot} + +* [lyuba](https://github.com/ringtailsoftware/lyuba) ## C# (.NET Standard) {#c-net-standard} +* [MastodonAPI](https://github.com/golf1052/MastodonAPI) * [Mastodot](https://github.com/yamachu/Mastodot) * [Mastonet](https://github.com/glacasa/Mastonet) * [TootNet](https://github.com/cucmberium/TootNet) @@ -23,26 +27,36 @@ menu: * [mastodonpp](https://schlomp.space/tastytea/mastodonpp) -## Crystal {#crystal} - -* [mastodon.cr](https://github.com/decors/mastodon.cr) - ## Common Lisp {#common-lisp} +* [mastodon-cl](https://github.com/compufox/mastodon-cl) * [tooter](https://github.com/Shinmera/tooter) -## Dart +## Crystal {#crystal} -* [mastodon](https://pub.dev/packages/mastodon) +* [mastodon-api-crystal](https://github.com/renatolond/mastodon-api-crystal) + +## Dart {#dart} + +* [mastodon_dart](https://pub.dev/packages/mastodon_dart) +* [mastodon-api](https://github.com/mastodon-dart/mastodon-api) +* [mastodon-oauth](https://github.com/mastodon-dart/mastodon-oauth2) +* [mastodon](https://github.com/mykdavies/Mastodon) +* [dartodon](https://github.com/darkcl/dartodon) ## Elixir {#elixir} * [hunter](https://github.com/milmazz/hunter) +## Erlang {#erlang} + +* [masterldon](https://github.com/igb/masterldon) + ## Go {#go} * [go-mastodon](https://github.com/mattn/go-mastodon) * [madon](https://github.com/McKael/madon) +* [go-mastodon-api](https://github.com/aaronland/go-mastodon-api) ## Haskell {#haskell} @@ -50,10 +64,13 @@ menu: ## Java {#java} -* [mastodon4j](https://github.com/sys1yagi/mastodon4j) +* [BigBone](https://github.com/andregasser/bigbone) +* [Mastodon4J](https://github.com/Mastodon4J/Mastodon4J) +* [mastodon-jfx](https://github.com/wakingrufus/mastodon-jfx) ## JavaScript {#javascript} +* [megalodon](https://github.com/h3poteto/megalodon) * [masto.js](https://github.com/neet/masto.js) * [libodonjs](https://github.com/Zatnosk/libodonjs) @@ -63,29 +80,51 @@ menu: ## JavaScript (Node.js) {#javascript-node-js} -* [node-mastodon](https://github.com/jessicahayley/node-mastodon) * [mastodon-api](https://github.com/vanita5/mastodon-api) +## Kotlin {#kotlin} + +* [BigBone](https://github.com/andregasser/bigbone) +* [mastodonk](https://github.com/outadoc/mastodonk) + +## Nim {#nim} + +* [Mastonim](https://github.com/matrix07012/Mastonim) + +## Objective-C {#objective-c} + +* [Cocotodon](https://github.com/shibafu528/Cocotodon) + ## Perl {#perl} * [Mastodon::Client](https://metacpan.org/pod/Mastodon::Client) ## PHP {#php} -* [Mastodon API for Laravel](https://github.com/kawax/laravel-mastodon-api) -* [Composer based php API wrapper](https://github.com/r-daneelolivaw/mastodon-api-php) -* [MastodonOAuthPHP](https://github.com/TheCodingCompany/MastodonOAuthPHP) +* [mastodon-api-client](https://github.com/vazaha-nl/mastodon-api-client) * [Phediverse Mastodon REST Client](https://github.com/phediverse/mastodon-rest) * [TootoPHP](https://framagit.org/MaxKoder/TootoPHP) * [oauth2-mastodon](https://github.com/lrf141/oauth2-mastodon) +* [MastodonBotPHP](https://github.com/Eleirbag89/MastodonBotPHP) +* [mastodon-api-php-oauth](https://github.com/yks118/Mastodon-api-php-oauth) +* [mastodon-api-php](https://github.com/colorfield/mastodon-api-php) +* [Mastodon API for Laravel](https://github.com/kawax/laravel-mastodon-api) +* [Mastodon for Drupal](https://www.drupal.org/project/mastodon) +* [Mastodon for Socialite](https://github.com/kawax/socialite-mastodon) + +## PowerShell {#powershell} + +* [Mastodon](https://github.com/JB405/Mastodon) ## Python {#python} * [Mastodon.py](https://github.com/halcy/Mastodon.py) +* [mastopy](https://gitlab.com/spla/mastopy) ## R {#r} * [mastodon](https://github.com/ThomasChln/mastodon) +* [rtoot](https://github.com/schochastics/rtoot) ## Ruby {#ruby} @@ -93,8 +132,8 @@ menu: ## Rust {#rust} -* [mammut](https://github.com/Aaronepower/mammut) (unmaintained) -* [elefren](https://github.com/DeeUnderscore/elefren) (unmaintained) +* [megalodon-rs](https://github.com/h3poteto/megalodon-rs) +* [mastodon-async](https://github.com/dscottboggs/mastodon-async) ## Scala {#scala} @@ -104,9 +143,17 @@ menu: ### Guile {#guile} -* [elefan](https://codeberg.org/WammKD/Guile-Mastodon) +* [Guile-Mastodon](https://codeberg.org/WammKD/Guile-Mastodon) ## Swift {#swift} -* [MastodonKit](https://github.com/ornithocoder/MastodonKit) +* [Mastodon.swift](https://github.com/Swiftodon/Mastodon.swift) +* [MastodonKit](https://github.com/MastodonKit/MastodonKit) +* [tootsdk](https://github.com/tootsdk/tootsdk) +* [MastodonAPI](https://github.com/li-bei/MastodonAPI) + +## TypeScript {#typescript} + +* [tsl-mastodon](https://github.com/typescriptlibs/tsl-mastodon-api) + diff --git a/content/en/client/public.md b/content/en/client/public.md index c7d4d314..3cc4897c 100644 --- a/content/en/client/public.md +++ b/content/en/client/public.md @@ -58,7 +58,7 @@ We can do similarly for hashtags by calling [GET /api/v1/timelines/tag/:hashtag] curl https://mastodon.example/api/v1/timelines/tag/cats?limit=2 ``` -We should once again see 2 statuses have been returned in a JSON array of [Status]({{< relref "entities/status" >}}) entities. We can parse the JSON by array, then by object. If we were using Python, our code might look something like this: +We should once again see that 2 statuses have been returned in a JSON array of [Status]({{< relref "entities/status" >}}) entities. We can parse the JSON by array, then by object. If we were using Python, our code might look something like this: ```python import requests @@ -71,7 +71,7 @@ print(statuses[0]["content"]) # this prints the status text ``` {{< hint style="info" >}} -Parsing JSON and using it in your program is outside of the scope of this tutorial, as it will be different depending on your choice of programming language and on the design of your program. Look for other tutorials on how to work with JSON in your programming language of choice. +Parsing JSON and using it in your program is outside of the scope of this tutorial, as it will be different depending on your choice of programming language and the design of your program. Look for other tutorials on how to work with JSON in your programming language of choice. {{< /hint >}} {{< hint style="info" >}} diff --git a/content/en/client/token.md b/content/en/client/token.md index 61d34913..b983c90e 100644 --- a/content/en/client/token.md +++ b/content/en/client/token.md @@ -11,7 +11,7 @@ menu: Up until this point, we've been working with publicly available information, but not all information is public. Some information requires permission before viewing it, in order to audit who is requesting that information (and to potentially revoke or deny access). -This is where [OAuth]({{< relref "spec/oauth" >}}) comes in. OAuth is a mechanism for generating access tokens which can be used to _authenticate (verify)_ that a request is coming from a specific client, and ensure that the requested action is _authorized (allowed)_ by the server's access control policies. +This is where [OAuth]({{< relref "spec/oauth" >}}) comes in. OAuth is a mechanism for generating access tokens that can be used to _authenticate (verify)_ that a request is coming from a specific client, and ensure that the requested action is _authorized (allowed)_ by the server's access control policies. ## Creating our application {#app} @@ -31,7 +31,7 @@ In the above example, we specify the client name and website, which will be show * `redirect_uris` has been set to the "out of band" token generation, which means that any generated tokens will have to be copied and pasted manually. The parameter is called `redirect_uris` because it is possible to define more than one redirect URI, but when generating the token, we will need to provide a URI that is included within this list. * `scopes` allow us to define what permissions we can request later. However, the requested scope later can be a subset of these registered scopes. See [OAuth Scopes]({{< relref "api/oauth-scopes" >}}) for more information. -We should see an Application entity returned, but for now we only care about client_id and client_secret. These values will be used to generate access tokens, so they should be cached for later use. See [POST /api/v1/apps]({{< relref "methods/apps#create" >}}) for more details on registering applications. +We should see an Application entity returned, but for now, we only care about client_id and client_secret. These values will be used to generate access tokens, so they should be cached for later use. See [POST /api/v1/apps]({{< relref "methods/apps#create" >}}) for more details on registering applications. ## Example authentication code flow {#flow} diff --git a/content/en/dev/code.md b/content/en/dev/code.md index 16a43490..cda075b5 100644 --- a/content/en/dev/code.md +++ b/content/en/dev/code.md @@ -9,7 +9,7 @@ menu: ### Code structure {#structure} -The following overview should not be seen as complete or authoritative, but as a rough guidance to help you find your way in the application. +The following overview should not be seen as complete or authoritative but as a rough guidance to help you find your way in the application. #### Ruby {#ruby} @@ -70,5 +70,4 @@ The following overview should not be seen as complete or authoritative, but as a All locale files are normalized to ensure consistent formatting and key order, which minimizes changesets in version control. - Run `bundle exec i18n-tasks normalize` to normalize server-side translations -- Run `yarn run manage:translations` to normalize client-side translations - +- Run `yarn run i18n:extract` to extract and normalize client-side translations into `en.json` diff --git a/content/en/dev/disclosure.md b/content/en/dev/disclosure.md index 3342a5b2..b6caaa33 100644 --- a/content/en/dev/disclosure.md +++ b/content/en/dev/disclosure.md @@ -7,7 +7,7 @@ menu: parent: dev --- -If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you should send the report to **hello@joinmastodon.org**. We will gladly reward such reports in proportion to the severity of the issue through our OpenCollective fund. +If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you should send the report to **security@joinmastodon.org**. We will gladly reward such reports in proportion to the severity of the issue through our OpenCollective fund. You should *not* report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk. diff --git a/content/en/dev/overview.md b/content/en/dev/overview.md index b7d64520..64b0759b 100644 --- a/content/en/dev/overview.md +++ b/content/en/dev/overview.md @@ -11,11 +11,11 @@ Mastodon is a Ruby on Rails application with a React.js front-end. It follows s The best way of working with Mastodon in a development environment is installing all the dependencies on your system, rather than using Docker or Vagrant. You need Ruby, Node.js, PostgreSQL and Redis, which is a pretty standard set of dependencies for Rails applications. -Tutorials for installing these dependencies can be found on the “Installing from source” page in the Running Mastodon section of the documentation. Please keep in mind that root access to a machine running Ubuntu 18.04 is required. After following the installation guide in the Running Mastodon section, see the “Setting up a dev environment” page for further instruction on how to configure your environment for development. +Tutorials for installing these dependencies can be found on the “Installing from source” page in the Running Mastodon section of the documentation. After following the installation guide in the Running Mastodon section, see the “Setting up a dev environment” page for further instructions on how to configure your environment for development. ### Environments {#environments} -An “environment” is a set of configuration values intended for a specific use case. Some environments could be: development, in which you intend to change the code; test, in which you intend to run the automated test suite; staging, which is meant to preview the code to end-users; and production, which is intended to face end-users. Mastodon comes with configurations for development, test and production. +An “environment” is a set of configuration values intended for a specific use case. Some environments could be: development, in which you intend to change the code; test, in which you intend to run the automated test suite; staging, which is meant to preview the code to end-users; and production, which is intended to face end-users. Mastodon comes with configurations for development, testing and production. The default value of `RAILS_ENV` is `development`, so you don’t need to set anything extra to run Mastodon in development mode. In fact, all of Mastodon’s configuration has correct defaults for the development environment, so you do not need an `.env` file unless you need to customize something. Here are some of the different behaviours between the development environment and the production environment: diff --git a/content/en/dev/routes.md b/content/en/dev/routes.md index 17e5de8f..17ef4395 100644 --- a/content/en/dev/routes.md +++ b/content/en/dev/routes.md @@ -7,7 +7,7 @@ menu: parent: dev --- -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/config/routes.rb" caption="config/routes.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/config/routes.rb" caption="config/routes.rb" >}} ## Explanation of routes {#routes} diff --git a/content/en/dev/setup.md b/content/en/dev/setup.md index 963eeab8..9cc318df 100644 --- a/content/en/dev/setup.md +++ b/content/en/dev/setup.md @@ -65,7 +65,7 @@ bundle config set --local path vendor/bundle On some operating systems, such as Debian and Fedora, `yarn` has been renamed to `yarnpkg`. On these systems, run `yarnpkg` instead of `yarn`. {{}} -In the development environment, Mastodon will use PostgreSQL as the currently signed-in Linux user using the `ident` method. Ensure that you have created a Postgres user and database for your current signed-in user: +In the development environment, Mastodon will use PostgreSQL as the currently signed-in Linux user using the `ident` method. Ensure that you have created a PostgreSQL user and database for your current signed-in user: ```sh sudo -u postgres createuser $USER --createdb @@ -95,6 +95,14 @@ You can now launch `http://localhost:3000` in your browser and log in with the d By default, Mastodon will run on port 3000. If you configure a different port for it, the generated admin account will use that number as well. {{}} +## Working with emails in development + +In development mode, Mastodon will use a gem called [Letter Opener](https://github.com/ryanb/letter_opener) for "sending" emails, which allows you to debug emails in your browser, without actually having to send emails via an SMTP server. + +In order to work with emails, you'll need Sidekiq, Redis and PostgreSQL running, and then emails can be viewed by visiting: `http://localhost:3000/letter_opener/` + +If you're developing in docker, you'll need to set the `REMOTE_DEV=true` environment variable. + ## Useful commands for testing {#testing} `rspec` diff --git a/content/en/entities/Account.md b/content/en/entities/Account.md index 967dd51a..eee5aea2 100644 --- a/content/en/entities/Account.md +++ b/content/en/entities/Account.md @@ -48,7 +48,7 @@ aliases: [ "followers_count": 547, "following_count": 404, "statuses_count": 28468, - "last_status_at": "2019-11-17T00:02:23.693Z", + "last_status_at": "2019-11-17", "emojis": [ { "shortcode": "ms_rainbow_flag", @@ -442,8 +442,8 @@ aliases: [ {{< page-relref ref="methods/accounts" caption="accounts API methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/serializers/rest/account_serializer.rb" caption="app/serializers/rest/account_serializer.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/account_serializer.rb" caption="app/serializers/rest/account_serializer.rb" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/serializers/rest/credential_account_serializer.rb" caption="app/serializers/rest/credential_account_serializer.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/credential_account_serializer.rb" caption="app/serializers/rest/credential_account_serializer.rb" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/serializers/rest/muted_account_serializer.rb" caption="app/serializers/rest/muted_account_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/muted_account_serializer.rb" caption="app/serializers/rest/muted_account_serializer.rb" >}} diff --git a/content/en/entities/Admin_Account.md b/content/en/entities/Admin_Account.md index 23ab6af5..a70293be 100644 --- a/content/en/entities/Admin_Account.md +++ b/content/en/entities/Admin_Account.md @@ -210,4 +210,4 @@ aliases: [ {{< page-relref ref="methods/admin/accounts" caption="admin/accounts API methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/serializers/rest/admin/account_serializer.rb" caption="app/serializers/rest/admin/account_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/account_serializer.rb" caption="app/serializers/rest/admin/account_serializer.rb" >}} \ No newline at end of file diff --git a/content/en/entities/Admin_Cohort.md b/content/en/entities/Admin_Cohort.md index 6a79d665..739f4e32 100644 --- a/content/en/entities/Admin_Cohort.md +++ b/content/en/entities/Admin_Cohort.md @@ -77,7 +77,7 @@ Daily retention data for the week between 2022-09-08 and 2022-09-14, given that "value": "1" } ] -}, +} ``` ## Attributes diff --git a/content/en/entities/Admin_Dimension.md b/content/en/entities/Admin_Dimension.md index 1b4cc919..4d67e6cb 100644 --- a/content/en/entities/Admin_Dimension.md +++ b/content/en/entities/Admin_Dimension.md @@ -165,7 +165,7 @@ Show dimensional data about how much space is used by each software in your serv "human_value": "0 Bytes" } ] -}, +} ``` ### `software_versions` {#software_versions} diff --git a/content/en/entities/Admin_DomainAllow.md b/content/en/entities/Admin_DomainAllow.md index 69ecc1b4..904210c8 100644 --- a/content/en/entities/Admin_DomainAllow.md +++ b/content/en/entities/Admin_DomainAllow.md @@ -53,4 +53,4 @@ aliases: [ {{< page-relref page="methods/admin/domain_allows" caption="admin/domain_allows API methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/domain_allows_serializer.rb" caption="app/serializers/rest/admin/domain_allows_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/domain_allow_serializer.rb" caption="app/serializers/rest/admin/domain_allow_serializer.rb" >}} \ No newline at end of file diff --git a/content/en/entities/Admin_DomainBlock.md b/content/en/entities/Admin_DomainBlock.md index 069780a1..4b19999a 100644 --- a/content/en/entities/Admin_DomainBlock.md +++ b/content/en/entities/Admin_DomainBlock.md @@ -104,4 +104,4 @@ aliases: [ {{< page-relref page="methods/admin/domain_blocks" caption="admin/domain_blocks API methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/domain_blocks_serializer.rb" caption="app/serializers/rest/admin/domain_blocks_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/domain_block_serializer.rb" caption="app/serializers/rest/admin/domain_block_serializer.rb" >}} \ No newline at end of file diff --git a/content/en/entities/Admin_EmailDomainBlock.md b/content/en/entities/Admin_EmailDomainBlock.md index 66e4bafe..1eb6f181 100644 --- a/content/en/entities/Admin_EmailDomainBlock.md +++ b/content/en/entities/Admin_EmailDomainBlock.md @@ -118,4 +118,4 @@ aliases: [ {{< page-relref page="methods/admin/email_domain_blocks" caption="admin/email_domain_blocks API methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/email_domain_blocks_serializer.rb" caption="app/serializers/rest/admin/email_domain_blocks_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/email_domain_block_serializer.rb" caption="app/serializers/rest/admin/email_domain_block_serializer.rb" >}} \ No newline at end of file diff --git a/content/en/entities/Admin_IpBlock.md b/content/en/entities/Admin_IpBlock.md index 53c2a6f8..129ee6c1 100644 --- a/content/en/entities/Admin_IpBlock.md +++ b/content/en/entities/Admin_IpBlock.md @@ -80,4 +80,4 @@ aliases: [ {{< page-relref page="methods/admin/ip_blocks" caption="admin/ip_blocks API methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/ip_blocks_serializer.rb" caption="app/serializers/rest/admin/ip_blocks_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/ip_block_serializer.rb" caption="app/serializers/rest/admin/ip_block_serializer.rb" >}} \ No newline at end of file diff --git a/content/en/entities/Admin_Report.md b/content/en/entities/Admin_Report.md index 894d7a6a..22f8cf11 100644 --- a/content/en/entities/Admin_Report.md +++ b/content/en/entities/Admin_Report.md @@ -170,7 +170,7 @@ aliases: [ {{< page-relref page="methods/admin/reports" caption="admin/reports API methods">}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/serializers/rest/admin/report_serializer.rb" caption="app/serializers/rest/admin/report_serializer.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/admin/report_serializer.rb" caption="app/serializers/rest/admin/report_serializer.rb" >}} diff --git a/content/en/entities/Application.md b/content/en/entities/Application.md index 5425466c..5ce71c8e 100644 --- a/content/en/entities/Application.md +++ b/content/en/entities/Application.md @@ -39,13 +39,6 @@ aliases: [ 0.9.9 - added\ 3.5.1 - this property is now nullable -### `vapid_key` {#vapid_key} - -**Description:** Used for Push Streaming API. Returned with [POST /api/v1/apps]({{< relref "methods/apps#create" >}}). Equivalent to [WebPushSubscription#server_key]({{< relref "entities/WebPushSubscription#server_key" >}})\ -**Type:** String\ -**Version history:**\ -2.8.0 - added - ### `client_id` {{%optional%}} {#client_id} **Description:** Client ID key, to be used for obtaining OAuth tokens\ @@ -60,10 +53,20 @@ aliases: [ **Version history:**\ 0.9.9 - added +## Deprecated attributes + +### `vapid_key` {#vapid_key} + +**Description:** Used for Push Streaming API. Returned with [POST /api/v1/apps]({{< relref "methods/apps#create" >}}). Equivalent to [WebPushSubscription#server_key]({{< relref "entities/WebPushSubscription#server_key" >}}) and [Instance#vapid_public_key]({{< relref "entities/Instance#vapid_public_key" >}})\ +**Type:** String\ +**Version history:**\ +2.8.0 - added +4.3.0 - deprecated pending removal + ## See also {{< page-relref ref="methods/apps" caption="apps API methods" >}} {{< page-relref ref="entities/Status#application" caption="Status (`application` attribute)" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/serializers/rest/application_serializer.rb" caption="app/serializers/rest/application_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/application_serializer.rb" caption="app/serializers/rest/application_serializer.rb" >}} diff --git a/content/en/entities/Error.md b/content/en/entities/Error.md index 67c29a09..6caa2126 100644 --- a/content/en/entities/Error.md +++ b/content/en/entities/Error.md @@ -117,7 +117,7 @@ Error: There was a temporary problem serving your request, please try again. App ## See also -{{< caption-link url="https://github.com/mastodon/mastodon/blob/master/app/controllers/api/base_controller.rb" caption="app/controllers/api/base_controller.rb" >}} +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/base_controller.rb" caption="app/controllers/api/base_controller.rb" >}} diff --git a/content/en/entities/FeaturedTag.md b/content/en/entities/FeaturedTag.md index 8c245e2d..44c93a9e 100644 --- a/content/en/entities/FeaturedTag.md +++ b/content/en/entities/FeaturedTag.md @@ -50,7 +50,7 @@ aliases: [ ### `statuses_count` {#statuses_count} **Description:** The number of authored statuses containing this hashtag.\ -**Type:** Number\ +**Type:** Integer\ **Version history:**\ 3.0.0 - added diff --git a/content/en/entities/FilterResult.md b/content/en/entities/FilterResult.md index d2323fbc..0a994e54 100644 --- a/content/en/entities/FilterResult.md +++ b/content/en/entities/FilterResult.md @@ -53,7 +53,7 @@ aliases: [ ### `status_matches` {#status_matches} **Description:** The status ID within the filter that was matched.\ -**Type:** {{}} String, or null\ +**Type:** {{}} Array of String, or null\ **Version history:**\ 4.0.0 - added @@ -63,4 +63,4 @@ aliases: [ {{< page-relref ref="methods/filters" caption="/api/v2/filters methods" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/filter_result_serializer.rb" caption="app/serializers/rest/filter_result_serializer.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/filter_result_serializer.rb" caption="app/serializers/rest/filter_result_serializer.rb" >}} diff --git a/content/en/entities/Instance.md b/content/en/entities/Instance.md index 18a9e23e..ab17c154 100644 --- a/content/en/entities/Instance.md +++ b/content/en/entities/Instance.md @@ -41,6 +41,9 @@ aliases: [ "urls": { "streaming": "wss://mastodon.social" }, + "vapid": { + "public_key": "BCkMmVdKDnKYwzVCDC99Iuc9GvId-x7-kKtuHnLgfF98ENiZp_aj-UNthbCdI70DqN1zUVis-x0Wrot2sBagkMc=" + }, "accounts": { "max_featured_tags": 10 }, @@ -290,6 +293,12 @@ aliases: [ **Version history:**\ 4.0.0 - added +### `configuration[vapid][public_key]` (#vapid_public_key) +**Description:** The instances VAPID public key, used for push notifications, the same as [WebPushSubscription#server_key]({{< relref "entities/WebPushSubscription#server_key" >}}).\ +**Type:** String\ +**Version history:**\ +4.3.0 - added + #### `configuration[accounts]` {#accounts} **Description:** Limits related to accounts.\ diff --git a/content/en/entities/MediaAttachment.md b/content/en/entities/MediaAttachment.md index eadd1bcd..51e3205e 100644 --- a/content/en/entities/MediaAttachment.md +++ b/content/en/entities/MediaAttachment.md @@ -212,7 +212,7 @@ More importantly, there may be another topl-level `focus` Hash object on images ### `description` {#description} **Description:** Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load.\ -**Type:** String\ +**Type:** {{}} String, or null if alternate text was not provided for the media attachment\ **Version history:**\ 2.0.0 - added diff --git a/content/en/entities/Reaction.md b/content/en/entities/Reaction.md index 455cec85..e45f970b 100644 --- a/content/en/entities/Reaction.md +++ b/content/en/entities/Reaction.md @@ -19,18 +19,20 @@ aliases: [ ## Example ```json -{ - "name": "bongoCat", - "count": 9, - "me": false, - "url": "https://files.mastodon.social/custom_emojis/images/000/067/715/original/fdba57dff7576d53.png", - "static_url": "https://files.mastodon.social/custom_emojis/images/000/067/715/static/fdba57dff7576d53.png" -}, -{ - "name": "🤔", - "count": 1, - "me": true -} +[ + { + "name": "bongoCat", + "count": 9, + "me": false, + "url": "https://files.mastodon.social/custom_emojis/images/000/067/715/original/fdba57dff7576d53.png", + "static_url": "https://files.mastodon.social/custom_emojis/images/000/067/715/static/fdba57dff7576d53.png" + }, + { + "name": "🤔", + "count": 1, + "me": true + } +] ``` ## Attributes diff --git a/content/en/entities/Relationship.md b/content/en/entities/Relationship.md index 592248fe..d513e5ed 100644 --- a/content/en/entities/Relationship.md +++ b/content/en/entities/Relationship.md @@ -134,6 +134,6 @@ aliases: [ ## See also -{{< page-relref ref="methods/accounts#relationships" caption="POST /api/v1/accounts/relationships" >}} +{{< page-relref ref="methods/accounts#relationships" caption="GET /api/v1/accounts/relationships" >}} {{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/serializers/rest/relationship_serializer.rb" caption="app/serializers/rest/relationship_serializer.rb" >}} diff --git a/content/en/entities/Role.md b/content/en/entities/Role.md index 381dde8d..0d8edb95 100644 --- a/content/en/entities/Role.md +++ b/content/en/entities/Role.md @@ -19,12 +19,9 @@ aliases: [ "id": 3, "name": "Owner", "color": "#ff3838", - "position": 1000, - "permissions": 1, - "highlighted": true, - "created_at": "2022-09-08T22:48:07.983Z", - "updated_at": "2022-09-08T22:48:07.983Z" -}, + "permissions": 1048575, + "highlighted": true +} ``` ## Attributes @@ -50,13 +47,6 @@ aliases: [ **Version history:**\ 4.0.0 - added -### `position` {#position} - -**Description:** An index for the role's position. The higher the position, the more priority the role has over other roles.\ -**Type:** Integer\ -**Version history:**\ -4.0.0 - added - ### `permissions` {#permissions} **Description:** A bitmask that represents the sum of all permissions granted to the role.\ @@ -71,20 +61,6 @@ aliases: [ **Version history:**\ 4.0.0 - added -### `created_at` {#created_at} - -**Description:** The date that the role was created.\ -**Type:** String (ISO 8601 Datetime)\ -**Version history:**\ -4.0.0 - added - -### `updated_at` {#created_at} - -**Description:** The date that the role was updated.\ -**Type:** String (ISO 8601 Datetime)\ -**Version history:**\ -4.0.0 - added - ## Permission flags To determine the permissions available to a certain role, convert the `permissions` attribute to binary and compare from the least significant bit upwards. For convenience (and to prevent the terms from growing too long), permissions will be presented below using hexadecimal values. @@ -93,7 +69,7 @@ To determine the permissions available to a certain role, convert the `permissio : **Administrator**. Users with this permission bypass all permissions. 0x2 -: **Devops**. Allows users to access Sidekiq and pgHero dashboards. +: **Devops**. Allows users to access Sidekiq and PgHero dashboards. 0x4 : **View Audit Log**. Allows users to see history of admin actions. diff --git a/content/en/entities/StatusEdit.md b/content/en/entities/StatusEdit.md index bc8f8453..c12996e5 100644 --- a/content/en/entities/StatusEdit.md +++ b/content/en/entities/StatusEdit.md @@ -78,7 +78,7 @@ aliases: [ ### `account` {#account} **Description:** The account that published this revision.\ -**Type:** Account\ +**Type:** [Account]({{}})\ **Version history:**\ 3.5.0 - added @@ -105,7 +105,7 @@ aliases: [ ### `media_attachments` {#media_attachments} -**Description:** The current state of the poll options at this revision. Note that edits changing the poll options will be collapsed together into one edit, since this action resets the poll.\ +**Description:** The current state of the media attachments at this revision.\ **Type:** Array of [MediaAttachment]({{}})\ **Version history:**\ 3.5.0 - added diff --git a/content/en/entities/Translation.md b/content/en/entities/Translation.md index 67d0c475..0b664c1c 100644 --- a/content/en/entities/Translation.md +++ b/content/en/entities/Translation.md @@ -10,25 +10,81 @@ aliases: [ ] --- -## Example +## Examples + +Translation of status with content warning and media ```json { - "content": "

Hola mundo

", - "detected_source_language": "en", + "content": "

Hello world

", + "spoiler_text": "Greatings ahead", + "media_attachments": [ + { + "id": 22345792, + "description": "Status author waving at the camera" + } + ], + "poll": null, + "detected_source_language": "es", "provider": "DeepL.com" } ``` +Translation of status with poll: +```json +{ + "content": "

Should I stay or should I go?

", + "spoiler_text": "", + "media_attachments": [], + "poll": [ + { + "id": 34858, + "options": [ + { + "title": "Stay" + }, + { + "title": "Go" + } + ] + } + ], + "detected_source_language": "ja", + "provider": "DeepL.com" +} +``` + + ## Attributes ### `content` {#content} -**Description:** The translated text of the status.\ +**Description:** HTML-encoded translated content of the status.\ **Type:** String (HTML)\ **Version history:**\ 4.0.0 - added +### `spoiler_warning` {#spoiler_warning} + +**Description:** The translated spoiler warning of the status.\ +**Type:** String\ +**Version history:**\ +4.2.0 - added + +### `poll` {#poll} + +**Description:** The translated poll options of the status.\ +**Type:** Array\ +**Version history:**\ +4.2.0 - added + +### `media_attachments` {#media_attachments} + +**Description:** The translated media descriptions of the status.\ +**Type:** Array\ +**Version history:**\ +4.2.0 - added + ### `detected_source_language` {#detected_source_language} **Description:** The language of the source text, as auto-detected by the machine translation provider.\ diff --git a/content/en/methods/accounts.md b/content/en/methods/accounts.md index 5803a249..9882005f 100644 --- a/content/en/methods/accounts.md +++ b/content/en/methods/accounts.md @@ -313,7 +313,10 @@ Update the user's display and preferences. 1.1.1 - added\ 2.3.0 - added `locked` parameter\ 2.4.0 - added `source[privacy,sensitive]` parameters\ -2.7.0 - added `discoverable` parameter +2.4.2 - added `source[language]` parameter\ +2.7.0 - added `discoverable` parameter\ +4.1.0 - added `hide_collections` parameter\ +4.2.0 - added `indexable` parameter #### Request @@ -345,6 +348,12 @@ bot discoverable : Boolean. Whether the account should be shown in the profile directory. +hide_collections +: Boolean. Whether to hide followers and followed accounts. + +indexable +: Boolean. Whether public posts should be searchable to anyone. + fields_attributes : Hash. The profile fields to be set. Inside this hash, the key is an integer cast to a string (although the exact integer does not matter), and the value is another hash including `name` and `value`. By default, max 4 fields. @@ -745,14 +754,14 @@ Authorization ##### Query parameters -max_id -: String. Return results older than this ID +max_id +: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results. since_id -: String. Return results newer than this ID +: String. All results returned will be greater than this ID. In effect, sets a lower bound on results. min_id -: String. Return results immediately newer than this ID +: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward. limit : Integer. Maximum number of results to return. Defaults to 20 statuses. Max 40 statuses. @@ -767,7 +776,7 @@ exclude_reblogs : Boolean. Filter out boosts from the response. pinned -: Boolean. Filter for pinned statuses only. +: Boolean. Filter for pinned statuses only. Defaults to false, which includes all statuses. Pinned statuses do not receive special priority in the order of the returned results. tagged : String. Filter for statuses using a specific hashtag. @@ -1940,7 +1949,8 @@ Find out whether a given account is followed, blocked, muted, etc. **Returns:** Array of [Relationship]({{< relref "entities/Relationship">}})\ **OAuth:** User token + `read:follows`\ **Version history:**\ -0.0.0 - added +0.0.0 - added\ +4.3.0 - added `with_suspended` parameter #### Request ##### Headers @@ -1951,7 +1961,10 @@ Authorization ##### Query parameters id[] -: Array. Check relationships for the provided account IDs. +: Array of String. Check relationships for the provided account IDs. + +with_suspended +: Boolean. Whether relationships should be returned for suspended users, defaults to false. #### Response ##### 200: OK @@ -2322,4 +2335,4 @@ Token does not have an authorized user {{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/v1/accounts/statuses_controller.rb" caption="app/controllers/api/v1/accounts/statuses_controller.rb" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/models/account_statuses_filter.rb" caption="app/models/account_statuses_filter.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/models/account_statuses_filter.rb" caption="app/models/account_statuses_filter.rb" >}} diff --git a/content/en/methods/admin/accounts.md b/content/en/methods/admin/accounts.md index e15a1419..e53c73ed 100644 --- a/content/en/methods/admin/accounts.md +++ b/content/en/methods/admin/accounts.md @@ -3,7 +3,7 @@ title: admin/accounts API methods description: Perform moderation actions with accounts. menu: docs: - name: admin/accounts + name: accounts parent: methods-admin identifier: methods-admin-accounts aliases: [ @@ -83,14 +83,14 @@ ip staff : Boolean. Filter for staff accounts? -max_id -: String. Return results older than ID. +max_id +: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results. since_id -: String. Return results newer than ID. +: String. All results returned will be greater than this ID. In effect, sets a lower bound on results. min_id -: String. Return results immediately newer than ID. +: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward. limit : Integer. Maximum number of results to return. Defaults to 100 accounts. Max 200 accounts. @@ -233,14 +233,14 @@ email ip : String. Lookup users with this IP address. -max_id -: String. Return results older than ID. +max_id +: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results. since_id -: String. Return results newer than ID. +: String. All results returned will be greater than this ID. In effect, sets a lower bound on results. min_id -: String. Return results immediately newer than ID. +: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward. limit : Integer. Maximum number of results to return. Defaults to 100 accounts. Max 200 accounts. @@ -686,7 +686,7 @@ POST /api/v1/admin/accounts/:id/action HTTP/1.1 Perform an action against an account and log this action in the moderation history. Also resolves any open reports against this account. -**Returns:** empty object\ +**Returns:** Empty\ **OAuth:** User token + `admin:write:accounts`\ **Permissions:** Manage Users, Manage Reports\ **Version history:**\ diff --git a/content/en/methods/admin/canonical_email_blocks.md b/content/en/methods/admin/canonical_email_blocks.md index c8cacb95..cdf44a7d 100644 --- a/content/en/methods/admin/canonical_email_blocks.md +++ b/content/en/methods/admin/canonical_email_blocks.md @@ -94,6 +94,11 @@ GET /api/v1/admin/canonical_email_blocks/:id HTTP/1.1 #### Request +##### Path parameters + +:id +: {{}} String. The ID of the Admin::CanonicalEmailBlock in the database. + ##### Headers Authorization @@ -262,6 +267,11 @@ DELETE /api/v1/admin/canonical_email_blocks/:id HTTP/1.1 #### Request +##### Path parameters + +:id +: {{}} String. The ID of the Admin::CanonicalEmailBlock in the database. + ##### Headers Authorization diff --git a/content/en/methods/admin/domain_blocks.md b/content/en/methods/admin/domain_blocks.md index 6034023e..f20ba884 100644 --- a/content/en/methods/admin/domain_blocks.md +++ b/content/en/methods/admin/domain_blocks.md @@ -3,7 +3,7 @@ title: admin/domain_blocks API methods description: Disallow certain domains to federate. menu: docs: - name: admin/domain_blocks + name: domain_blocks parent: methods-admin identifier: methods-admin-domain_blocks aliases: [ diff --git a/content/en/methods/admin/reports.md b/content/en/methods/admin/reports.md index f174ea80..669a5036 100644 --- a/content/en/methods/admin/reports.md +++ b/content/en/methods/admin/reports.md @@ -3,7 +3,7 @@ title: admin/reports API methods description: Perform moderation actions with reports. menu: docs: - name: admin/reports + name: reports parent: methods-admin identifier: methods-admin-reports aliases: [ diff --git a/content/en/methods/admin/trends.md b/content/en/methods/admin/trends.md index 868bad58..957fd45c 100644 --- a/content/en/methods/admin/trends.md +++ b/content/en/methods/admin/trends.md @@ -3,7 +3,7 @@ title: admin/trends API methods description: TODO menu: docs: - name: admin/trends + name: trends parent: methods-admin identifier: methods-admin-trends aliases: [ diff --git a/content/en/methods/apps.md b/content/en/methods/apps.md index 1abce816..d1dd5b26 100644 --- a/content/en/methods/apps.md +++ b/content/en/methods/apps.md @@ -84,7 +84,7 @@ GET /api/v1/apps/verify_credentials HTTP/1.1 Confirm that the app's OAuth2 credentials work. **Returns:** [Application]({{< relref "entities/application" >}}), but without `client_id` or `client_secret`\ -**OAuth level:** App token\ +**OAuth level:** App token + `read`\ **Version history:**\ 2.0.0 - added\ 2.7.2 - now returns `vapid_key` @@ -94,7 +94,7 @@ Confirm that the app's OAuth2 credentials work. ##### Headers Authorization -: {{}} Provide this header with `Bearer ` to gain authorized access to this API method. +: {{}} Provide this header with `Bearer ` to gain authorized access to this API method. #### Response ##### 200: OK @@ -125,4 +125,4 @@ If the Authorization header contains an invalid token, is malformed, or is not p {{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/v1/apps_controller.rb" caption="app/controllers/api/v1/apps_controller.rb" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/v1/apps/credentials_controller.rb" caption="app/controllers/api/v1/apps/credentials_controller.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/v1/apps/credentials_controller.rb" caption="app/controllers/api/v1/apps/credentials_controller.rb" >}} diff --git a/content/en/methods/conversations.md b/content/en/methods/conversations.md index b7e5cd5d..e5d0de51 100644 --- a/content/en/methods/conversations.md +++ b/content/en/methods/conversations.md @@ -128,7 +128,7 @@ DELETE /api/v1/conversations/:id HTTP/1.1 Removes a conversation from your list of conversations. -**Returns:** empty object\ +**Returns:** Empty\ **OAuth:** User token + `write:conversations`\ **Version history:**\ 2.6.0 - added diff --git a/content/en/methods/domain_blocks.md b/content/en/methods/domain_blocks.md index 71aa12ab..b61907be 100644 --- a/content/en/methods/domain_blocks.md +++ b/content/en/methods/domain_blocks.md @@ -90,7 +90,7 @@ Block a domain to: - remove all followers from it - prevent following new users from it (but does not remove existing follows) -**Returns:** empty object\ +**Returns:** Empty\ **OAuth:** User token + `write:blocks` or `follow`\ **Version:**\ 1.4.0 - added @@ -154,7 +154,7 @@ DELETE /api/v1/domain_blocks HTTP/1.1 Remove a domain block, if it exists in the user's array of blocked domains. -**Returns:** empty object\ +**Returns:** Empty\ **OAuth:** User token + `write:blocks` or `follow`\ **Version history:**\ 1.4.0 - added diff --git a/content/en/methods/emails.md b/content/en/methods/emails.md index 747662ab..baad47aa 100644 --- a/content/en/methods/emails.md +++ b/content/en/methods/emails.md @@ -21,10 +21,10 @@ aliases: [ ## Resend confirmation email {#confirmation} ```http -POST /api/v1/emails/confirmation HTTP/1.1 +POST /api/v1/emails/confirmations HTTP/1.1 ``` -**Returns:** Empty object\ +**Returns:** Empty\ **OAuth:** User token issued to the client that created the unconfirmed user\ **Version history:**\ 3.4.0 - added @@ -73,4 +73,4 @@ Alternatively, the user has already confirmed their email. {{< page-relref ref="methods/accounts#create" caption="POST /api/v1/accounts" >}} -{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/v1/emails/confirmations_controller.rb" caption="app/controllers/api/v1/emails/confirmations_controller.rb" >}} \ No newline at end of file +{{< caption-link url="https://github.com/mastodon/mastodon/blob/main/app/controllers/api/v1/emails/confirmations_controller.rb" caption="app/controllers/api/v1/emails/confirmations_controller.rb" >}} diff --git a/content/en/methods/endorsements.md b/content/en/methods/endorsements.md index bbedf350..902a313f 100644 --- a/content/en/methods/endorsements.md +++ b/content/en/methods/endorsements.md @@ -29,8 +29,7 @@ Accounts that the user is currently featuring on their profile. **Returns:** Array of [Account]({{< relref "entities/account" >}})\ **OAuth:** User token + `read:accounts`\ **Version history:**\ -2.5.0 - added\ -3.3.0 - both `min_id` and `max_id` can be used at the same time now +2.5.0 - added #### Request diff --git a/content/en/methods/featured_tags.md b/content/en/methods/featured_tags.md index 793bf62e..15ad5c31 100644 --- a/content/en/methods/featured_tags.md +++ b/content/en/methods/featured_tags.md @@ -133,7 +133,7 @@ DELETE /api/v1/featured_tags/:id HTTP/1.1 Stop promoting a hashtag on your profile. -**Returns:** empty object\ +**Returns:** Empty\ **OAuth:** User token + `write:accounts`\ **Version history:**\ 3.0.0 - added diff --git a/content/en/methods/filters.md b/content/en/methods/filters.md index d6621031..0ad24a23 100644 --- a/content/en/methods/filters.md +++ b/content/en/methods/filters.md @@ -16,7 +16,7 @@ aliases: [ ## Server-side (v2) methods {#v2} -Since Mastodon 3.6, filters can contain multiple keywords and are matched server-side. Clients apply the filter action based on [the status's `filtered` attribute]({{< relref "entities/Status#filtered" >}}). +Since Mastodon 4.0, filters can contain multiple keywords and are matched server-side. Clients apply the filter action based on [the status's `filtered` attribute]({{< relref "entities/Status#filtered" >}}). --- @@ -206,7 +206,7 @@ keywords_attributes[][keyword] : String. A keyword to be added to the newly-created filter group. keywords_attributes[][whole_word] -: String. Whether the keyword should consider word boundaries. +: Boolean. Whether the keyword should consider word boundaries.