Update documentation with contents by twrnh

This commit is contained in:
Eugen Rochko 2020-01-01 22:37:59 +01:00
parent 92e8aadd7f
commit 33cb69ad07
324 changed files with 18140 additions and 9654 deletions

View File

@ -1,4 +1,4 @@
image: registry.gitlab.com/pages/hugo:latest
image: monachus/hugo:latest
variables:
GIT_SUBMODULE_STRATEGY: recursive

13
assets/main.js Normal file
View File

@ -0,0 +1,13 @@
(function () {
'use strict';
const onLoaded = () => {
// Nothing for now
};
if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
onLoaded();
} else {
document.addEventListener('DOMContentLoaded', onLoaded);
}
})();

View File

@ -1,5 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600|Roboto:400,500,700|Roboto+Mono:400');
@import url('https://unpkg.com/ionicons@4.2.0/dist/css/ionicons.min.css');
@import 'fontawesome.scss';
$white: #fff ; // color5
$lightest: #d9e1e8; // color2
@ -123,13 +123,11 @@ body {
justify-content: flex-start;
align-items: center;
color: $darkest;
margin-top: 12px;
margin-bottom: 52px;
img {
height: 38px;
position: relative;
left: -14px;
top: -4px;
}
@media screen and (max-width: $mobile-width) {
@ -141,30 +139,26 @@ body {
}
}
a {
display: inline-block;
color: $lighter;
& > ul > li {
margin-bottom: 26px;
}
.sub-title {
display: block;
padding: 10px;
font-weight: 500;
text-transform: uppercase;
}
& > ul a {
display: block;
color: $white;
text-decoration: none;
font-size: 14px;
padding: 10px 0;
&:hover,
&:focus,
&:active {
color: lighten($lighter, 8%);
}
}
& > ul > li > a {
font-size: 18px;
font-family: 'Montserrat', sans-serif;
font-weight: 600;
padding-bottom: 26px;
font-weight: 500;
padding: 10px;
}
.sub-menu {
padding-bottom: 26px;
a.active {
color: $vibrant;
}
@ -172,6 +166,10 @@ body {
&.collapsed {
display: none;
}
.sub-menu {
padding-left: 15px;
}
}
}
@ -424,6 +422,10 @@ main {
overflow: auto;
}
figure pre {
margin-bottom: 0;
}
code {
padding: 0.2em 0.4em;
margin: 0;
@ -569,3 +571,205 @@ main {
}
}
}
.hint {
margin: 26px 0;
padding: 16px 16px 16px 56px;
position: relative;
border-left: 4px solid;
border-radius: 4px;
background-color: lighten($darkest, 8%);
color: $white;
font-size: 16px;
line-height: 28px;
&-info {
border-color: $vibrant;
.hint-icon {
color: $vibrant;
}
}
&-warning {
border-color: #ca8f04;
.hint-icon {
color: #ca8f04;
}
}
&-success {
border-color: $success;
.hint-icon {
color: $success;
}
}
&-danger {
border-color: $error;
.hint-icon {
color: $error;
}
}
&-icon {
position: absolute;
top: 18px;
left: 16px;
font-size: 24px;
}
}
.page-ref {
display: block;
background: lighten($darkest, 4%);
color: $vibrant;
text-decoration: none;
font-weight: 500;
font-size: 18px;
position: relative;
margin: 26px 0;
padding: 24px 24px 24px 50px;
border-radius: 4px;
&-icon {
position: absolute;
top: 20px;
left: 16px;
font-size: 24px;
}
&:hover,
&:focus,
&:active {
background: lighten($darkest, 8%);
}
}
.api-method {
display: flex;
align-items: center;
font-weight: 500;
font-size: 18px;
&-method {
display: inline-flex;
align-items: center;
padding: 2px 0;
border-radius: 9999px;
height: 21px;
color: $white;
background: $vibrant;
margin: 0;
margin-right: 8px;
span {
display: inline-block;
padding: 0 8px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
line-height: 15px;
}
}
&-url {
background: lighten($darkest, 4%);
border-radius: 4px;
padding: 8px;
font-size: 16px;
margin: 26px 0;
line-height: 1.7;
}
&-host {
color: $darker;
}
&-path {
font-weight: 700;
color: $white;
}
}
.api-method-parameters-list {
border: 1px solid lighten($darkest, 8%);
border-radius: 4px;
margin-bottom: 26px;
}
.api-method-parameters-type {
font-weight: 400;
color: $darker;
}
.api-method-parameters-type,
.api-method-response-example h5 {
font-size: 16px;
margin-bottom: 16px;
}
.api-method-parameter {
display: flex;
border-bottom: 1px solid lighten($darkest, 8%);
&:last-child {
border-bottom: 0;
}
&-cell {
box-sizing: border-box;
padding: 16px 10px;
color: $white;
}
&-required {
text-transform: uppercase;
font-size: 12px;
font-weight: 500;
color: $error;
}
&-optional {
text-transform: uppercase;
font-size: 12px;
font-weight: 500;
color: $darker;
}
&-name {
width: 180px;
font-size: 16px;
& > div:first-child {
margin-bottom: 2px;
}
}
&-type {
width: 100px;
}
&-description {
flex: 1 1 0%;
font-size: 15px;
}
}
.api-method-response-example {
&-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
background: $success;
&-error {
background: $error;
}
}
}

View File

@ -8,69 +8,50 @@ metaDataFormat = "yaml"
paginate = 100
enableGitInfo = true
[menu]
[[menu.docs]]
name = "Using Mastodon"
weight = 10
identifier = "user"
url = "/user/"
[[menu.docs]]
name = "Running Mastodon"
weight = 20
identifier = "admin"
url = "/admin/"
[[menu.docs]]
name = "Developing Mastodon apps"
weight = 30
identifier = "client"
url = "/client/"
[[menu.docs]]
name = "Contributing to Mastodon"
weight = 40
identifier = "dev"
url = "/dev/"
[[menu.docs]]
name = "Spec compliance"
weight = 50
identifier = "spec"
url = "/spec/"
[[menu.docs]]
name = "REST API"
weight = 60
identifier = "api"
url = "/api/"
[[menu.docs]]
name = "API Methods"
weight = 70
identifier = "methods"
url = "/methods/"
[[menu.docs]]
name = "API Entities"
weight = 80
identifier = "entities"
url = "/entities/"
[languages]
[languages.en]
contentDir = "content/en"
languageName = "English"
weight = 1
[languages.pl]
contentDir = "content/pl"
languageName = "Polski"
weight = 1
[languages.fr]
contentDir = "content/fr"
languageName = "Français"
weight = 1
[languages.fr.menu]
[[languages.fr.menu.docs]]
name = "Guide d'utilisation"
weight = 1
identifier = "usage"
url = "/usage/"
[[languages.fr.menu.docs]]
name = "Guide d'administration"
weight = 2
identifier = "administration"
url = "/administration/"
[[languages.fr.menu.docs]]
name = "Guide de développement"
weight = 3
identifier = "development"
url = "/development/"
[[languages.fr.menu.docs]]
name = "Aperçu de l'API"
weight = 4
identifier = "api"
url = "/api/"
[[languages.fr.menu.docs]]
name = "API REST"
weight = 5
identifier = "rest-api"
url = "/api/rest/"
[menu]
[[menu.docs]]
name = "User guide"
weight = 1
identifier = "usage"
url = "/usage/"
[[menu.docs]]
name = "Administrator guide"
weight = 2
identifier = "administration"
url = "/administration/"
[[menu.docs]]
name = "API Overview"
weight = 4
identifier = "api"
url = "/api/"
[[menu.docs]]
name = "REST API"
weight = 5
identifier = "rest-api"
url = "/api/rest/"
[[menu.docs]]
name = "Development guide"
weight = 3
identifier = "development"
url = "/development/"
weight = -99

View File

@ -1,15 +1,113 @@
---
title: Mastodon documentation
title: What is Mastodon?
description: Welcome to the Mastodon documentation!
menu:
docs:
weight: -99
---
Welcome to the Mastodon documentation!
{{< youtube id="IPSbNdBmWKE" caption="An introductory video explaining basic Mastodon concepts with visual animations" >}}
## What is a microblog?
Similar to how blogging is the act of publishing updates to a website, **microblogging** is the act of publishing small updates to a stream of updates on your profile. You can publish text posts and optionally attach media such as pictures, audio, video, or polls. Mastodon lets you follow friends and discover new ones.
## What is federation? <a id="what-is-federation"></a>
**Federation** is a form of decentralization. Instead of a single central service that all people use, there are multiple services, that any number of people can use.
| Grade of centralization | Examples |
| :--- | :--- |
| Centralized | Twitter, Facebook, Instagram |
| Federated | Email, XMPP, phone networks, physical mail |
| Distributed | BitTorrent, IPFS, Scuttlebutt |
A Mastodon website can operate alone. Just like a traditional website, people sign up on it, post messages, upload pictures and talk to each other. _Unlike_ a traditional website, Mastodon websites can interoperate, letting their users communicate with each other; just like you can send an email from your Gmail account to someone from Outlook, Fastmail, Protonmail, or any other email provider, as long as you know their email address, **you can mention or message anyone on any website using their address**.
{{< figure src="/assets/image%20%289%29.png" caption="From left to right: Centralized, Federated, Distributed" >}}
## What is ActivityPub? <a id="the-fediverse"></a>
Mastodon uses a standardized, open protocol to implement federation. It is called **ActivityPub**. Any software that likewise implements federation via ActivityPub can seamlessly communicate with Mastodon, just like Mastodon websites communicate with one another.
The **fediverse** \(“federated universe”\) is the name for all websites that can communicate with each other over ActivityPub and the World Wide Web. That includes all Mastodon servers, but also other implementations:
* Pleroma, a modular microblogging engine,
* Pixelfed, which lets you share and consume media posts,
* Misskey, which includes microblogging alongside a customizable dashboard,
* PeerTube, which lets you upload videos to channels,
* Plume, which lets you publish longer-form articles,
* and many more, including individual and personal websites!
The fediverse does not have its own brand, so you might more often hear “follow me on Mastodon” than “follow me on the fediverse”, but technically the latter is more correct.
## Practical implications <a id="practical-implications"></a>
### Choice of service provider and policy
Because Mastodon is simply software that can be used to power any website, potential users of Mastodon have the option of choosing a service provider from already-existing Mastodon websites, or to create their own Mastodon website if they wish. The Mastodon project maintains a list of recommended service providers at [joinmastodon.org](https://joinmastodon.org), sortable by category and/or language. Some websites may have moderation policies that go beyond this, such as requiring the use of certain tags on potentially sensitive content, and some websites may have more relaxed moderation policies, but websites listed in the picker all agree to adopt the [Mastodon Server Covenant](https://joinmastodon.org/covenant), meaning that they pledge to actively moderate against hate speech, to take daily backups, to have at least one emergency admin, and to provide at least 3 months advance notice in case of shutdown.
> Maintaining communities that feel safe for all of its members is not easy. Mastodon provides a lot of foundational framework and tools for doing it, and shifts the power to effect change from one commercial entity to the communities themselves.
>
> -- Eugen Rochko, Jul 6 2018, ["Cage the Mastodon"](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
> A centralized social media platform has a hierarchical structure where rules and their enforcement, as well as the development and direction of the platform, are decided by the CEO \[...\] A decentralized network deliberately relinquishes control of the platform owner, by essentially not having one.
>
> -- Eugen Rochko, Dec 30 2018, ["Why does decentralization matter?"](https://blog.joinmastodon.org/2018/12/why-does-decentralization-matter/)
### Funding and monetization <a id="funding-and-monetization"></a>
Mastodon websites are operated by different people or organizations completely independently. Mastodon does not implement any monetization strategies in the software.
Some server operators choose to offer paid accounts, some server operators are companies who can utilize their existing infrastructure, some server operators rely on crowdfunding from their users via Patreon and similar services, and some server operators are just paying out-of-pocket for a personal server for themselves and maybe some friends. So if you want to support the server hosting your account, check if it offers a way to donate.
Mastodon development is likewise crowdfunded via [Patreon](https://patreon.com/mastodon) and via [OpenCollective](https://opencollective.com/mastodon). **No venture capital is involved.**
> In my opinion, “instant, public, global messaging and conversation” should, in fact, be _global_. Distributed between independent organizations and actors who can self-govern. A public utility, without incentives to exploit the conversations for profit.
>
> -- Eugen Rochko, Mar 3 2018, ["Twitter is not a public utility"](https://blog.joinmastodon.org/2018/03/twitter-is-not-a-public-utility/)
### Interoperability between different software <a id="impersonation-and-verification"></a>
In practical terms: Imagine if you could follow an Instagram user from your Twitter account and comment on their photos without leaving your account. If Twitter and Instagram were federated services that used the same protocol, that would be possible. With a Mastodon account, **you can communicate with any other compatible website,** _**even if it is not running on Mastodon**_. All that is necessary is that the software support the same subset of the ActivityPub protocol that allows for creating and interacting with status updates. To find out more about the technical specifications required to interoperate with Mastodon, see [ActivityPub](spec/activitypub.md), [WebFinger](spec/webfinger.md), and [Security](spec/security.md). To read more about what ActivityPub allows us to do, see [Why ActivityPub is the future](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/).
> All of these platforms are different and they focus on different needs. And yet, the foundation is all the same: people subscribing to receive posts from other people. And so, they are all compatible.
>
> -- Eugen Rochko, Jun 27 2018, ["Why ActivityPub is the future"](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
### Free/libre software
Unlike proprietary services, **anyone has the complete freedom to run, examine, inspect, copy, modify, distribute, and reuse the Mastodon source code, provided they guarantee the same freedoms for any derivative work.** Just like how users of Mastodon can choose their service provider, you as an individual are free to contribute features to Mastodon or publish a modified version of Mastodon that includes different features. These modified versions, also known as software forks, are required to also uphold the same freedoms as the original Mastodon project. For example, [glitch-soc](https://glitch-soc.github.io/docs/) is a software distribution that adds various experimental features. Many individual forks exist as well, perhaps themed slightly differently or including small modifications to the codebase. Because Mastodon is libre software that respects your freedom, personalizations like this are not only allowed but encouraged.
> The ultimate power is in giving people the ability to create their own spaces, their own communities, to modify the software as they see fit, but without sacrificing the ability of people from different communities to interact with each other.
>
> -- Eugen Rochko, Feb 20 2017, ["The power to build communities: A response to Mark Zuckerberg"](https://blog.joinmastodon.org/2017/02/the-power-to-build-communities/)
> Decentralization is biodiversity of the digital world, the hallmark of a healthy ecosystem. A decentralized network like the fediverse allows different user interfaces, different software, different forms of government to co-exist and cooperate.
>
> -- Eugen Rochko, Dec 30 2018, ["Why does decentralization matter?"](https://blog.joinmastodon.org/2018/12/why-does-decentralization-matter/)
## Choose your path
![](/assets/elephant.svg)
Learn how to use Mastodon:
{{< page-ref page="user/signup.md" >}}
Learn how to install Mastodon:
{{< page-ref page="admin/prerequisites.md" >}}
Learn how to write an app for Mastodon:
{{< page-ref page="client/intro.md" >}}
Learn about the Mastodon backend and how to contribute:
{{< page-ref page="dev/overview.md" >}}
<div style="margin-bottom: 26px">
{{< youtube "IPSbNdBmWKE" >}}
</div>
**Choose your path:**
- [Learn how to use Mastodon]({{< relref "usage/basics.md" >}})
- [Learn how to install Mastodon]({{< relref "administration/installation.md" >}})
- [Learn how to write an app for Mastodon]({{< relref "api/guidelines.md" >}})

View File

@ -0,0 +1,55 @@
---
title: Backing up your server
menu:
docs:
weight: 80
parent: admin
---
## Setting up regular backups \(optional, but not really\) <a id="setting-up-regular-backups-optional-but-not-really"></a>
For any real-world use, you should make sure to regularly backup your Mastodon server.
### Overview <a id="overview"></a>
Things that need to be backed up in order of importance:
1. PostgreSQL database
2. Application secrets from the `.env.production` file or equivalent
3. User-uploaded files
4. Redis database
### Failure modes <a id="failure-modes"></a>
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.
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.
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 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.
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 wont be of much use.
### Backing up application secrets <a id="backing-up-application-secrets"></a>
Application secrets are the easiest to backup, since they never change. You only need to store `.env.production` somewhere safe.
### Backing up PostgreSQL <a id="backing-up-postgresql"></a>
PostgreSQL is at risk of data corruption from power cuts, hard disk drive failure, and botched schema migrations. For that reason, occassionally making a backup with `pg_dump` or `pg_dumpall` is recommended.
For high-availability setups, it is possible to use hot streaming replication to have a second PostgreSQL server with always up-to-date data, ready to be switched over to if the other server goes down.
### Backing up user-uploaded files <a id="backing-up-user-uploaded-files"></a>
If you are using an external object storage provider such as Amazon S3, Google Cloud or Wasabi, then you dont need to worry about backing those up. The respective companies are responsible for handling hardware failures.
If you are using local file storage, then its up to you to make copies of the sizeable `public/system` directory, where uploaded files are stored by default.
### Backing up Redis <a id="backing-up-redis"></a>
Backing up Redis is easy. Redis regularly writes to `/var/lib/redis/dump.rdb` which is the only file you need to make a copy of.

222
content/en/admin/config.md Normal file
View File

@ -0,0 +1,222 @@
---
title: Configuring variables
menu:
docs:
weight: 30
parent: admin
---
{{< hint style="warning" >}}
This page is under construction.
{{< /hint >}}
Mastodon uses environment variables as its configuration.
For convenience, it can read them from a flat file called `.env.production` in the Mastodon directory, 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.
### Basic <a id="basic"></a>
#### Federation <a id="federation"></a>
* `LOCAL_DOMAIN`
* `WEB_DOMAIN`
* `ALTERNATE_DOMAINS`
#### Secrets <a id="secrets"></a>
* `SECRET_KEY_BASE`
* `OTP_SECRET`
* `VAPID_PRIVATE_KEY`
* `VAPID_PUBLIC_KEY`
#### Deployment <a id="deployment"></a>
* `RAILS_ENV`
* `RAILS_SERVE_STATIC_FILES`
* `RAILS_LOG_LEVEL`
* `TRUSTED_PROXY_IP`
* `SOCKET`
* `PORT`
* `NODE_ENV`
* `BIND`
#### Scaling options <a id="scaling-options"></a>
* `WEB_CONCURRENCY`
* `MAX_THREADS`
* `PREPARED_STATEMENTS`
* `STREAMING_API_BASE_URL`
* `STREAMING_CLUSTER_NUM`
### Database connections <a id="database-connections"></a>
#### PostgreSQL <a id="postgresql"></a>
* `DB_HOST`
* `DB_USER`
* `DB_NAME`
* `DB_PASS`
* `DB_PORT`
* `DATABASE_URL`
#### Redis <a id="redis"></a>
* `REDIS_HOST`
* `REDIS_PORT`
* `REDIS_URL`
* `REDIS_NAMESPACE`
* `CACHE_REDIS_HOST`
* `CACHE_REDIS_PORT`
* `CACHE_REDIS_URL`
* `CACHE_REDIS_NAMESPACE`
#### ElasticSearch <a id="elasticsearch"></a>
* `ES_ENABLED`
* `ES_HOST`
* `ES_PORT`
* `ES_PREFIX`
#### StatsD <a id="statsd"></a>
* `STATSD_ADDR`
* `STATSD_NAMESPACE`
### Limits <a id="limits"></a>
* `SINGLE_USER_MODE`
* `EMAIL_DOMAIN_WHITELIST`
* `DEFAULT_LOCALE`
* `MAX_SESSION_ACTIVATIONS`
* `USER_ACTIVE_DAYS`
### E-mail <a id="e-mail"></a>
* `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_TLS`
### File storage <a id="file-storage"></a>
* `CDN_HOST`
* `S3_ALIAS_HOST`
#### Local file storage <a id="local-file-storage"></a>
* `PAPERCLIP_ROOT_PATH`
* `PAPERCLIP_ROOT_URL`
#### Amazon S3 and compatible <a id="amazon-s3-and-compatible"></a>
* `S3_ENABLED`
* `S3_BUCKET`
* `AWS_ACCESS_KEY_ID`
* `AWS_SECRET_ACCESS_KEY`
* `S3_REGION`
* `S3_PROTOCOL`
* `S3_HOSTNAME`
* `S3_ENDPOINT`
* `S3_SIGNATURE_VERSION`
#### Swift <a id="swift"></a>
* `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 <a id="external-authentication"></a>
* `OAUTH_REDIRECT_AT_SIGN_IN`
#### LDAP <a id="ldap"></a>
* `LDAP_ENABLED`
* `LDAP_HOST`
* `LDAP_PORT`
* `LDAP_METHOD`
* `LDAP_BASE`
* `LDAP_BIND_DN`
* `LDAP_PASSWORD`
* `LDAP_UID`
* `LDAP_SEARCH_FILTER`
#### PAM <a id="pam"></a>
* `PAM_ENABLED`
* `PAM_EMAIL_DOMAIN`
* `PAM_DEFAULT_SERVICE`
* `PAM_CONTROLLED_SERVICE`
#### CAS <a id="cas"></a>
* `CAS_ENABLED`
* `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`
#### SAML <a id="saml"></a>
* `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 <a id="hidden-services"></a>
* `http_proxy`
* `ALLOW_ACCESS_TO_HIDDEN_SERVICE`
### Other <a id="other"></a>
* `SKIP_POST_DEPLOYMENT_MIGRATIONS`

205
content/en/admin/install.md Normal file
View File

@ -0,0 +1,205 @@
---
title: Installing from source
menu:
docs:
weight: 20
parent: admin
---
## Pre-requisites <a id="pre-requisites"></a>
* A machine running **Ubuntu 18.04** that you have root access to
* A **domain name** \(or a subdomain\) for the Mastodon server, e.g. `example.com`
* An e-mail delivery service or other **SMTP server**
You will be running the commands as root. If you arent already root, switch to root:
### System repositories <a id="system-repositories"></a>
Make sure curl is installed first:
#### Node.js <a id="node-js"></a>
```bash
curl -sL https://deb.nodesource.com/setup_8.x | bash -
```
#### Yarn <a id="yarn"></a>
```bash
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
```
### System packages <a id="system-packages"></a>
```bash
apt update
apt install -y \
imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core \
g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf \
bison build-essential libssl-dev libyaml-dev libreadline6-dev \
zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev \
nginx redis-server redis-tools postgresql postgresql-contrib \
certbot python-certbot-nginx yarn libidn11-dev libicu-dev libjemalloc-dev
```
### Installing Ruby <a id="installing-ruby"></a>
We will be using rbenv to manage Ruby versions, because its 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:
```bash
adduser --disabled-login mastodon
```
We can then switch to the user:
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
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 2.6.1
rbenv global 2.6.1
```
Default gem version shipped with ruby\_2.6.0 is incompatible with latest bundler, so we need to update gem:
```bash
gem update --system
```
Well also need to install bundler:
```bash
gem install bundler --no-document
```
Return to the root user:
## Setup <a id="setup"></a>
### Setting up PostgreSQL <a id="setting-up-postgresql"></a>
#### Performance configuration \(optional\) <a id="performance-configuration-optional"></a>
For optimal performance, you may use [pgTune](https://pgtune.leopard.in.ua/#/) to generate an appropriate configuration and edit values in `/etc/postgresql/9.6/main/postgresql.conf` before restarting PostgreSQL with `systemctl restart postgresql`
#### Creating a user <a id="creating-a-user"></a>
You will need to create a PostgreSQL user that Mastodon could use. It is easiest to go with “ident” authentication in a simple setup, i.e. the PostgreSQL user does not have a separate password and can be used by the Linux user with the same username.
Open the prompt:
In the prompt, execute:
```sql
CREATE USER mastodon CREATEDB;
\q
```
Done!
### Setting up Mastodon <a id="setting-up-mastodon"></a>
It is time to download the Mastodon code. Switch to the mastodon user:
#### Checking out the code <a id="checking-out-the-code"></a>
Use git to download the latest stable release of Mastodon:
```bash
git clone https://github.com/tootsuite/mastodon.git live && cd live
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
```
#### Installing the last dependencies <a id="installing-the-last-dependencies"></a>
Now to install Ruby and JavaScript dependencies:
```bash
bundle install \
-j$(getconf _NPROCESSORS_ONLN) \
--deployment --without development test
yarn install --pure-lockfile
```
#### Generating a configuration <a id="generating-a-configuration"></a>
Run the interactive setup wizard:
```bash
RAILS_ENV=production bundle exec rake mastodon:setup
```
This will:
* Create a configuration file
* Run asset precompilation
* Create the database schema
The configuration file is saved as `.env.production`. You can review and edit it to your liking. Refer to the [documentation on configuration.]({{< relref "config.md" >}})
Youre done with the mastodon user for now, so switch back to root:
### Setting up nginx <a id="setting-up-nginx"></a>
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
```
Then edit `/etc/nginx/sites-available/mastodon` to replace `example.com` with your own domain name, and make any other adjustments you might need.
Reload nginx for the changes to take effect:
### Acquiring a SSL certificate <a id="acquiring-a-ssl-certificate"></a>
Well use Lets 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 havent started the Mastodon process yet.
### Setting up systemd services <a id="setting-up-systemd-services"></a>
Copy the systemd service templates from the Mastodon directory:
```bash
cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
```
Then edit the files to make sure the username and paths are correct:
* `/etc/systemd/system/mastodon-web.service`
* `/etc/systemd/system/mastodon-sidekiq.service`
* `/etc/systemd/system/mastodon-streaming.service`
Finally, start and enable the new systemd services:
```bash
systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
systemctl enable mastodon-*
```
They will now automatically start at boot time.
{{< hint style="success" >}}
**Hurray! This is it. You can visit your domain in the browser now!**
{{< /hint >}}

View File

@ -0,0 +1,93 @@
---
title: Migrating to a new machine
menu:
docs:
weight: 90
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.
{{< hint style="info" >}}
This guide was written with Ubuntu Server in mind; your mileage may vary for other setups.
{{< /hint >}}
## Basic steps <a id="basic-steps"></a>
1. Set up a new Mastodon server using the [Production Guide]({{< relref "install.md" >}}) \(however, dont 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.
4. Copy the `system/` files using the instructions below. \(Note: if youre 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.
11. Enjoy your new server!
## Detailed steps <a id="detailed-steps"></a>
### What data needs to be migrated <a id="what-data-needs-to-be-migrated"></a>
At a high level, youll need to copy over the following:
* The `~/live/public/system` directory, which contains user-uploaded images and videos \(if using S3, you dont need this\)
* The Postgres 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, youll probably also want to copy the following for convenience:
* The nginx config \(under `/etc/nginx/sites-available/default`\)
* The systemd config files \(`/etc/systemd/system/mastodon-*.service`\), which may contain your server tweaks and customizations
* The pgbouncer configuration under `/etc/pgbouncer` \(if youre using it\)
### Dump and load Postgres <a id="dump-and-load-postgres"></a>
Instead of running `mastodon:setup`, were 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)\).
Run this as the `mastodon` user on your old system:
```bash
pg_dump -Fc mastodon_production -f backup.dump
```
Copy the `backup.dump` file over, using `rsync` or `scp`. Then on the new system, create an empty database as the `mastodon` user:
```bash
createdb -T template0 mastodon_production
```
Then import it:
```bash
pg_restore -U mastodon -n public --no-owner --role=mastodon \
-d mastodon_production backup.dump
```
\(Note that if the username is not `mastodon` on the new server, you should change the `-U` AND `--role` values above. Its okay if the username is different between the two servers.\)
### Copy files <a id="copy-files"></a>
This will probably take some time, and youll want to avoid re-copying unnecessarily, so using `rsync` is recommended. On your old machine, as the `mastodon` user, run:
```bash
rsync -avz ~/live/public/system/ mastodon@example.com:~/live/public/system/
```
Youll 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.
### During migration <a id="during-migration"></a>
You can edit the `~/live/public/500.html` page on the old machine if you want to show a nice error message to let existing users know that a migration is in progress.
Youll probably also want to set the DNS TTL to something small \(30-60 minutes\) about a day in advance, so that DNS can propagate quickly once you point it to the new IP address.
### After migrating <a id="after-migrating"></a>
You can check [whatsmydns.net](https://whatsmydns.net/) to see the progress of DNS propagation. To jumpstart the process, you can always edit your own `/etc/hosts` file to point to your new server so you can start playing around with it early.

View File

@ -1,61 +1,61 @@
---
title: Moderation
description: Overview of moderation tools on Mastodon
title: Moderation actions
menu:
docs:
parent: usage
weight: 4
weight: 110
parent: admin
---
## Individual moderation
## Individual moderation <a id="individual-moderation"></a>
Moderation in Mastodon is always applied locally, i.e. as seen from the particular server. An admin or moderator on one server cannot affect a user on another server, they can only affect the local copy on their own server.
### Disable login
### Disable login <a id="disable-login"></a>
A Mastodon account can be disabled. 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 re-enabled at any time. This limitation is only available for local users on your server.
### Silence
### Silence <a id="silence"></a>
A Mastodon silence is synonymous with sandbox. A silenced account does not appear to users who are not already following it. All of the content is still there, and it can still be found via search, mentioned, and followed, but the content is invisible.
At this moment, silence does not affect federation. A locally silenced account is *not* silenced automatically on other servers.
At this moment, silence does not affect federation. A locally silenced account is _not_ silenced automatically on other servers.
This limitation is reversible, the account can be unsilenced at any time.
### Suspend
### Suspend <a id="suspend"></a>
A Mastodon suspension is synonymous with deletion. The account no longer appears in search, the profile page is gone, all of the posts, uploads, followers, and all other data is removed. This limitation is **irreversible**. While the account can be unsuspended, allowing the user to take control of it again, the old data is gone for good.
## Server-wide moderation
## Server-wide moderation <a id="server-wide-moderation"></a>
Because individually moderating a large volume of users from a misbehaving server can be exhausting, it is possible to pre-emptively moderate against all users from that particular server using a so-called **domain block**, which comes with several different levels of severity.
### Reject media
### Reject media <a id="reject-media"></a>
With this option active, no files from the server will be processed locally. That includes avatars, headers, emojis and media attachments.
### Silence
### Silence <a id="silence-1"></a>
Applies a silence to all past and future accounts from the server.
### Suspend
### Suspend <a id="suspend-1"></a>
Applies a suspension to all past and future accounts from the server. No content from the server will be stored locally except for usernames.
## Spam-fighting measures
## Spam-fighting measures <a id="spam-fighting-measures"></a>
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
* 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.
### Blocking by e-mail server
### Blocking by e-mail server <a id="blocking-by-e-mail-server"></a>
Spammers will often use different e-mail domains so it looks like they are using a lot of different e-mail servers that would all be difficult to blacklist separately. However, sometimes all of those domains resolve to a single e-mail server IP. If you see a lot of spammers signing up at the same time, you can check for this, either using an online DNS lookup tool, or the Linux `dig` utility, e.g. `dig 1.2.3.4` will return all DNS records for that IP. If you notice the IP is the same for all domains, you can add it to the e-mail domain blacklist.
### Blocking by IP
### Blocking by IP <a id="blocking-by-ip"></a>
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`:
@ -71,3 +71,4 @@ sudo iptables -I INPUT 1 -m set --match-set spambots src -j DROP
```
Be careful not to lock yourself out of your machine.

View File

@ -0,0 +1,9 @@
---
title: Installing optional features
menu:
docs:
weight: 40
parent: admin
identifier: admin-optional
---

View File

@ -0,0 +1,78 @@
---
title: Full-text search
menu:
docs:
weight: 10
parent: admin-optional
---
Mastodon supports full-text search when it ElasticSearch is available. Mastodons full-text search allows logged in users to find results from their own toots, their favourites, and their mentions. It deliberately does not allow searching for arbitrary strings in the entire database.
### Installing ElasticSearch <a id="install-elasticsearch"></a>
ElasticSearch requires a Java runtime. If you dont have Java already installed, do it now. Assuming you are logged in as `root`:
```bash
apt install openjdk-8-jre-headless
```
Add the official ElasticSearch repository to apt:
```bash
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-6.x.list
apt update
```
Now you can install ElasticSearch:
```bash
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 its 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](). If you have a multi-host setup, you must know how to secure internal traffic.
{{< /hint >}}
To start ElasticSearch:
```bash
systemctl enable elasticsearch
systemctl start elasticsearch
```
### Configuring Mastodon <a id="setup-mastodon"></a>
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 index, you can set `ES_PREFIX` directly.
After saving the new configuration, create the index in ElasticSearch with:
```bash
RAILS_ENV=production bundle exec rake chewy:upgrade
```
Then restart Mastodon processes for the new configuration to take effect:
```bash
systemctl restart mastodon-sidekiq
systemctl reload mastodon-web
```
Now new statuses will be written to the ElasticSearch index. The last step is importing all of the old data as well. This might take a long while:
```bash
RAILS_ENV=production bundle exec rake chewy:sync
```
{{< hint style="warning" >}}
**Compatibility note:** There is a known bug in Ruby 2.6.0 that prevents the above task from working. Other versions of Ruby, such as 2.6.1, are fine.
{{< /hint >}}

View File

@ -0,0 +1,12 @@
---
title: Single Sign On
menu:
docs:
weight: 30
parent: admin-optional
---
{{< hint style="danger" >}}
This page is under construction.
{{< /hint >}}

View File

@ -1,75 +1,18 @@
---
title: Optional features
description: How to enable Mastodon's optional features
title: Hidden services
menu:
docs:
parent: administration
weight: 5
weight: 20
parent: admin-optional
---
## Full-text search
Mastodon can be served through Tor as an onion service. This will give you a \*.onion address that can only be used while connected to the Tor network.
Mastodon supports full-text search when it ElasticSearch is available. Mastodon's full-text search allows logged in users to find results from their own toots, their favourites, and their mentions. It deliberately does not allow searching for arbitrary strings in the entire database.
## Installing Tor
### Install ElasticSearch
First Tors Debian archive needs to be added to apt.
ElasticSearch requires a Java runtime. If you don't have Java already installed, do it now. Assuming you are logged in as `root`:
apt install openjdk-8-jre-headless
Add the official ElasticSearch repository to apt:
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-6.x.list
apt update
Now you can install ElasticSearch:
apt install elasticsearch
> **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]({{< relref "installation.md" >}}). If you have a multi-host setup, you must know how to secure internal traffic.
To start ElasticSearch:
systemctl enable elasticsearch
systemctl start elasticsearch
### Setup Mastodon
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 index, you can set `ES_PREFIX` directly.
After saving the new configuration, create the index in ElasticSearch with:
RAILS_ENV=production bundle exec rake chewy:upgrade
Then restart Mastodon processes for the new configuration to take effect:
systemctl restart mastodon-sidekiq
systemctl reload mastodon-web
Now new statuses will be written to the ElasticSearch index. The last step is importing all of the old data as well. This might take a long while:
RAILS_ENV=production bundle exec rake chewy:sync
> **Compatibility note:** There is a known bug in Ruby 2.6.0 that prevents the above task from working. Other versions of Ruby, such as 2.6.1, are fine.
## Hidden services
Mastodon can be served through Tor as an onion service. This will give you a *.onion address that can only be used while connected to the Tor network.
### Installing Tor
First Tor's Debian archive needs to be added to apt.
```
```text
deb https://deb.torproject.org/torproject.org stretch main
deb-src https://deb.torproject.org/torproject.org stretch main
```
@ -86,11 +29,11 @@ Finally install the required packages.
apt install tor deb.torproject.org-keyring
```
### Configure Tor
## Configure Tor
Edit the file at `/etc/tor/torrc` and add the following configuration.
```bash
```text
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServiceVersion 3
HiddenServicePort 80 127.0.0.1:80
@ -104,13 +47,13 @@ sudo service tor restart
Your tor hostname can now be found at `/var/lib/tor/hidden_service/hostname`.
### Move your Mastodon configuration
## Move your Mastodon configuration
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.
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.
```
```text
add_header Referrer-Policy "same-origin";
keepalive_timeout 70;
@ -129,7 +72,7 @@ In place of your old Mastodon configuration add an include directive to this new
Your Nginx configuration file will be left looking something like this.
```
```text
server {
listen 80;
server_name mastodon.myhosting.com;
@ -152,13 +95,13 @@ server {
}
```
### Serve Tor over http
## Serve Tor over 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/p/o44SnkW2.html).
The solution is to serve your Mastodon instance over http, but only for Tor. This can be added by pre-pending an additional configuration to your Nginx configuration.
```
```text
server {
listen 80;
server_name mastodon.qKnFwnNH2oH4QhQ7CoRf7HYj8wCwpDwsa8ohJmcPG9JodMZvVA6psKq7qKnFwnNH2oH4QhQ7CoRf7HYj8wCwpDwsa8ohJmcPG9JodMZvVA6psKq7.onion;
@ -189,7 +132,7 @@ server {
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 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.
@ -199,13 +142,9 @@ Restart your web server.
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.
## Login via LDAP/PAM/CAS/SAML
TODO
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 users level of caution.

View File

@ -0,0 +1,96 @@
---
title: Preparing your machine
menu:
docs:
weight: 10
parent: admin
---
If you are setting up a fresh machine, it is recommended that you secure it first. Assuming that you are running **Ubuntu 18.04**:
## 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.
Edit `/etc/ssh/sshd_config` and find `PasswordAuthentication`. Make sure its uncommented and set to `no`. If you made any changes, restart sshd:
## Update system packages
```bash
apt update && apt upgrade -y
```
## Install fail2ban so it blocks repeated login attempts
Edit `/etc/fail2ban/jail.local` and put this inside:
```text
[DEFAULT]
destemail = your@email.here
sendername = Fail2Ban
[sshd]
enabled = true
port = 22
[sshd-ddos]
enabled = true
port = 22
```
Finally restart fail2ban:
```bash
systemctl restart fail2ban
```
## Install a firewall and only whitelist SSH, HTTP and HTTPS ports
First, install iptables-persistent. During installation it will ask you if you want to keep current rulesdecline.
```bash
apt install -y iptables-persistent
```
Edit `/etc/iptables/rules.v4` and put this inside:
```text
*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# 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
# Allow SSH connections
# The -dport number should be the same port number you set in sshd_config
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Reject all other inbound - default deny unless explicitly allowed policy
-A INPUT -j REJECT
-A FORWARD -j REJECT
COMMIT
```
With iptables-persistent, that configuration will be loaded at boot time. But since we are not rebooting right now, we need to load it manually for the first time:
```bash
iptables-restore < /etc/iptables/rules.v4
```

281
content/en/admin/scaling.md Normal file
View File

@ -0,0 +1,281 @@
---
title: Scaling up your server
menu:
docs:
weight: 100
parent: admin
---
## Managing concurrency <a id="managing-concurrency"></a>
Mastodon has three types of processes:
* Web \(Puma\)
* Streaming API
* Background processing \(Sidekiq\)
### Web \(Puma\) <a id="web-puma"></a>
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
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.
These values affect how many HTTP requests can be served at the same time.
In terms of throughput, more processes are better than more threads.
### Streaming API <a id="streaming-api"></a>
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
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.
### Background processing \(Sidekiq\) <a id="background-processing-sidekiq"></a>
Many tasks in Mastodon are delegated to background processing to ensure the HTTP requests are fast, and to prevent HTTP request aborts from affecting the execution of those tasks. Sidekiq is a single process, with a configurable number of threads.
#### Number of threads <a id="number-of-threads"></a>
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.:
```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.
#### Queues <a id="queues"></a>
Sidekiq uses different queues for tasks of varying importance, where importance is defined by how much it would impact the user experience of your servers local users if the queue wasnt 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` | Fetching information from other servers |
The default queues and their priorities are stored in `config/sidekiq.yml`, but can be overridden by the command-line invocation of Sidekiq, e.g.:
```bash
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.
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.
## Transaction pooling with pgBouncer <a id="transaction-pooling-with-pgbouncer"></a>
### Why you might need PgBouncer <a id="why-you-might-need-pgbouncer"></a>
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.
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.
### Installing PgBouncer <a id="installing-pgbouncer"></a>
On Debian and Ubuntu:
```bash
sudo apt install pgbouncer
```
### Configuring PgBouncer <a id="configuring-pgbouncer"></a>
#### Setting a password <a id="setting-a-password"></a>
First off, if your `mastodon` user in Postgres is set up wthout a password, you will need to set a password.
Heres how you might reset the password:
```bash
psql -p 5432 -U mastodon mastodon_production -w
```
Then \(obviously, use a different password than the word “password”\):
```sql
ALTER USER mastodon WITH PASSWORD 'password';
```
Then `\q` to quit.
#### Configuring userlist.txt <a id="configuring-userlist-txt"></a>
Edit `/etc/pgbouncer/userlist.txt`
As long as you specify a user/password in pgbouncer.ini later, the values in userlist.txt do _not_ have to correspond to real PostgreSQL roles. You can arbitrarily define users and passwords, but you can reuse the “real” credentials for simplicitys sake. Add the `mastodon` user to the `userlist.txt`:
```text
"mastodon" "md5d75bb2be2d7086c6148944261a00f605"
```
Here were using the md5 scheme, where the md5 password is just the md5sum of `password + username` with the string `md5` prepended. For instance, to derive the hash for user `mastodon` with password `password`, you can do:
```bash
# ubuntu, debian, etc.
echo -n "passwordmastodon" | md5sum
# macOS, openBSD, etc.
md5 -s "passwordmastodon"
```
Then just add `md5` to the beginning of that.
Youll also want to create a `pgbouncer` admin user to log in to the PgBouncer admin database. So heres a sample `userlist.txt`:
```text
"mastodon" "md5d75bb2be2d7086c6148944261a00f605"
"pgbouncer" "md5a45753afaca0db833a6f7c7b2864b9d9"
```
In both cases the password is just `password`.
#### Configuring pgbouncer.ini <a id="configuring-pgbouncer-ini"></a>
Edit `/etc/pgbouncer/pgbouncer.ini`
Add a line under `[databases]` listing the Postgres databases you want to connect to. Here well just have PgBouncer use the same username/password and database name to connect to the underlying Postgres 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:
```text
listen_addr = 127.0.0.1
listen_port = 6432
```
Put `md5` as the `auth_type` \(assuming youre using the md5 format in `userlist.txt`\):
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 youll want to change the `pool_mode` from `session` to `transaction`:
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.\)
The defaults are fine to start, and you can always increase them later:
```text
max_client_conn = 100
default_pool_size = 20
```
Dont forget to reload or restart pgbouncer after making your changes:
```bash
sudo systemctl reload pgbouncer
```
#### Debugging that it all works <a id="debugging-that-it-all-works"></a>
You should be able to connect to PgBouncer just like you would with Postgres:
```bash
psql -p 6432 -U mastodon mastodon_production
```
And then use your password to log in.
You can also check the PgBouncer logs like so:
```bash
tail -f /var/log/postgresql/pgbouncer.log
```
#### Configuring Mastodon to talk to PgBouncer <a id="configuring-mastodon-to-talk-to-pgbouncer"></a>
In your `.env.production` file, first off make sure that this is set:
```bash
PREPARED_STATEMENTS=false
```
Since were using transaction-based pooling, we cant use prepared statements.
Next up, configure Mastodon to use port 6432 \(PgBouncer\) instead of 5432 \(Postgres\) and you should be good to go:
```bash
DB_HOST=localhost
DB_USER=mastodon
DB_NAME=mastodon_production
DB_PASS=password
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 its different, etc\)
{{< /hint >}}
#### Administering PgBouncer <a id="administering-pgbouncer"></a>
The easiest way to reboot is:
```bash
sudo systemctl restart pgbouncer
```
But if youve set up a PgBouncer admin user, you can also connect as the admin:
```bash
psql -p 6432 -U pgbouncer pgbouncer
```
And then do:
```sql
RELOAD;
```
Then use `\q` to quit.
## Separate Redis for cache <a id="separate-redis-for-cache"></a>
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 thats important data you wouldnt 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.
As far as configuring the Redis database goes, basically you can get rid of background saving to disk, since it doesnt 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)
## Read-replicas <a id="read-replicas"></a>
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:
* The streaming API server does not issue writes at all, so you can connect it straight to the replica. But its 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. Lets talk about that.
You will have to edit the `config/database.yml` file and replace the `production` section as follows:
```yaml
production:
<<: *default
adapter: postgresql_makara
prepared_statements: false
makara:
id: postgres
sticky: true
connections:
- role: master
blacklist_duration: 0
url: postgresql://db_user:db_password@db_host:db_port/db_name
- role: slave
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).
{{< 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.
{{< /hint >}}

45
content/en/admin/setup.md Normal file
View File

@ -0,0 +1,45 @@
---
title: Setting up your new instance
menu:
docs:
weight: 50
parent: admin
---
## Creating an admin account
### In the browser <a id="in-the-browser"></a>
After signing up in the browser, you will need to use the command line to give your newly created account admin privileges. Assuming your username is `alice`:
```bash
RAILS_ENV=production bin/tootctl accounts modify alice --role admin
```
### From the command line <a id="from-the-command-line"></a>
You can create a new account using the command-line interface.
```bash
RAILS_ENV=production bin/tootctl accounts create \
alice \
--email alice@example.com \
--confirmed \
--role admin
```
A randomly generated password will be shown in the terminal.
## Filling in server information <a id="filling-in-server-information"></a>
After logging in, navigate to the **Site settings** page. While there are no technical requirements for filling in this information, it is considered crucial for operating a server for humans.
| Setting | Meaning |
| :--- | :--- |
| Contact username | Your username so people know who owns the server |
| Business e-mail | An e-mail address so people locked out of their accounts, or people without accounts, can contact you |
| Instance description | Why did you start this server? Who is it for? What makes it different? |
| Custom extended information | You can put all sorts of information in here but a **code of conduct** is recommended |
After you fill these in, simply hit “Save changes”.

452
content/en/admin/tootctl.md Normal file
View File

@ -0,0 +1,452 @@
---
title: Using the admin CLI
description: tootctl commands that can be run from the CLI.
menu:
docs:
weight: 60
parent: admin
---
---
The command-line interface of Mastodon is an executable file called `tootctl` residing in the `bin` directory within the Mastodon root directory. You must specify which environment you intend to use whenever you execute it by specifying the `RAILS_ENV` environment variable. Unless you are a developer working on a local machine, you need to use `RAILS_ENV=production`. If you are sure that you will never need another environment \(for development, testing, or staging\), you can add it to your `.bashrc` file for convenience, e.g.:
```bash
echo "export RAILS_ENV=production" >> ~/.bashrc
```
If so, you wont need to specify it each time inline. Otherwise, calls to `tootctl` will usually go like this, assuming that the Mastodon code is checked out in `/home/mastodon/live`:
```bash
cd /home/mastodon/live
RAILS_ENV=production bin/tootctl help
```
## Base CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/cli.rb" caption="lib/cli.rb" >}}
### `tootctl self-destruct`
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.
{{< 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.
{{< /hint >}}
**Version history:**
2.8.0 - added
| Option | Description |
| :--- | :--- |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl --version`
Show the version of the currently running Mastodon instance.
**Version history:**
2.7.0 - added
## Accounts CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/accounts_cli.rb" caption="lib/mastodon/accounts\_cli.rb" >}}
### `tootctl accounts rotate`
Generate and broadcast new RSA keys, as part of security maintenance.
**Version history:**
2.5.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username for an account. |
| --all | Can be provided instead of USERNAME to rotate keys for all local accounts. |
### `tootctl accounts create`
Create a new user account with given USERNAME and provided --email.
**Version history:**
2.6.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username for the new account. Required. |
| --email EMAIL | Email address to be attached to the user. Required. |
| --confirmed | Skip sending the confirmation email and activate the account immediately. |
| --role ROLE | Define the new account as a `user`, `moderator`, or `admin`. Defaults to `user`. |
| --reattach | Reuse an old USERNAME after its account has been deleted. |
| --force | Forcefully delete any existing account with this USERNAME and reattach the new account in place of the \(just-deleted\) account. |
### `tootctl accounts modify`
Modify a user account's role, email, active status, approval mode, or 2FA requirement.
**Version history:**
2.6.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username for the new account. Required. |
| --role ROLE | Define the account as a `user`, `moderator`, or `admin`. |
| --email EMAIL | Update the user's email address to EMAIL. |
| --confirm | Skip confirmation email, when used with --email. |
| --disable | Lock USERNAME out of their account. |
| --enable | Unlock USERNAME's account if it is currently disabled. |
| --approve | Approve the account, if you are/were in approval mode. |
| --disable\_2fa | Remove additional factors and allow login with password. |
### `tootctl accounts delete`
Delete a user account with given USERNAME.
**Version history:**
2.6.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username for the account. Required. |
### `tootctl 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.
**Version history:**
2.6.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username for the account. Required. |
### `tootctl accounts cull`
Remove remote accounts that no longer exist. Queries every single remote account in the database to determine if it still exists on the origin server, and if it doesn't, then remove it from the database. Accounts that have had confirmed activity within the last week are excluded from the checks, in case the server is just down.
**Version history:**
2.6.0 - added
2.8.0 - add `--dry_run`
| Option | Description |
| :--- | :--- |
| --concurrency N | The number of workers to use for this task. Defaults to N=5. |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl accounts refresh`
Refetch remote user data and files for one or multiple accounts.
**Version history:**
2.6.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username |
| --all | Can be provided instead of USERNAME to refresh all remote accounts. |
| --domain DOMAIN | Can be provided instead of USERNAME. Operate only on remote accounts from this DOMAIN. |
| --concurrency N | The number of workers to use for this task. Defaults to N=5. |
| --verbose | Print additional information while task is processing. |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl accounts follow`
Force all local accounts to follow a local account specified by username.
**Version history:**
2.7.0 - added
3.0.0 - now uses USERNAME instead of ACCT
| Option | Description |
| :--- | :--- |
| USERNAME | Local username |
| --concurrency N | The number of workers to use for this task. Defaults to N=5. |
| --verbose | Print additional information while task is processing. |
### `tootctl accounts unfollow`
Force all local accounts to unfollow an account specified by their address.
**Version history:**
2.7.0 - added
| Option | Description |
| :--- | :--- |
| ACCT | `username@domain` address |
| --concurrency N | The number of workers to use for this task. Defaults to N=5. |
| --verbose | Print additional information while task is processing. |
### `tootctl accounts reset-relationships`
Reset all follow and/or follower relationships for a local account.
**Version history:**
2.8.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username |
| --follows | Force USERNAME to unfollow everyone, then re-follow them. |
| --followers | Remove all of USERNAME's followers. |
### `tootctl accounts approve`
Approve new registrations when instance is in approval mode.
**Version history:**
2.8.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Approve the pending account with this username |
| --number N | Approve the N most recent registrations. |
| --all | Approve all pending registrations. |
## Cache CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/cache_cli.rb" caption="lib/mastodon/cache\_cli.rb" >}}
### `tootctl cache clear`
Clear out the cache storage.
**Version history:**
2.8.1 - added
### `tootctl cache recount`
Update hard-cached counters of TYPE by counting referenced records from scratch. It may take a very long time to finish, depending on the size of the database. Accounts will have their follower, following, and status counts refreshed. Statuses will have their reply, boost, and favourite counts refreshed.
**Version history:**
3.0.0 - added
| Option | Description |
| :--- | :--- |
| TYPE | Either `accounts` or `statuses` |
| --concurrency N | The number of workers to use for this task. Defaults to N=5. |
| --verbose | Print additional information while task is processing. |
## Domains CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/domains_cli.rb" caption="lib/mastodon/domains\_cli.rb" >}}
### `tootctl domains purge`
Remove all accounts from a given DOMAIN without leaving behind any records. Unlike a suspension, if the DOMAIN still exists in the wild, it means the accounts could return if they are resolved again.
**Version history:**
2.6.0 - added
2.8.0 - add `--whitelist_mode`
2.9.0 - remove custom emoji as well
3.0.0 - accept multiple domains
| Option | Description |
| :--- | :--- |
| DOMAIN\[...\] | Domains to purge, separated by space. |
| --whitelist\_mode | Can be provided instead of DOMAIN. Instead of purging from a single domain, all accounts from domains that are not whitelisted will be removed from the database. Use this after enabling whitelist mode and defining your whitelist. |
| --concurrency N | The number of workers to use for this task. Defaults to 5. |
| --verbose | Print additional information while task is processing. |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl domains crawl`
Crawl the known fediverse by using Mastodon REST API endpoints that expose all known peers, and collect statistics from those peers, as long as those peers support those API endpoints. When no START is given, the command uses the server's own database of known peers to seed the crawl. Returns total servers, total registered users, total active users in the last week, and total users joined in the last week.
**Version history:**
2.7.0 - added
3.0.0 - add `--exclude_suspended`
| Option | Description |
| :--- | :--- |
| START | Optionally start from a different domain name. |
| --concurrency N | The number of workers to use for this task. Defaults to 50. |
| --format FORMAT | Control how results are returned. `summary` will print a summary. `domains` will return a newline-delimited list of all discovered peers. `json` will dump aggregated raw data. Defaults to `summary`. |
| --exclude\_suspended | Do not include instances that you have suspended in the output. Also includes any subdomains. |
## Emoji CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/emoji_cli.rb" caption="lib/mastodon/emoji\_cli.rb" >}}
### `tootctl emoji import`
Imports custom emoji from a .tar.gz archive at a given path. The archive should contain PNG or GIF files no larger than 50KB, and the shortcode will be set equal to the filename minus the extension, with optional prefixes and/or suffixes.
**Version history:**
2.5.0 - added
| Option | Description |
| :--- | :--- |
| PATH | Path to a .tar.gz archive containing pictures. |
| --prefix PREFIX | Add PREFIX to the beginning of generated shortcodes. |
| --suffix SUFFIX | Add SUFFIX to the end of generated shortcodes. |
| --overwrite | Instead of skipping existing emoji, replace them with any discovered emoji with the same shortcode. |
| --unlisted | Processed emoji will not be shown in the emoji picker, but will be usable only by their direct shortcode. |
| --category CATEGORY | Group the processed emoji under CATEGORY in the picker. |
### `tootctl emoji purge`
Remove all custom emoji.
**Version history:**
2.8.0 - added
## Feeds CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/feeds_cli.rb" caption="lib/mastodon/feeds\_cli.rb" >}}
### `tootctl feeds build`
Build home and list feeds for one or all users. Feeds will be built from the database and cached in-memory with Redis. Mastodon manages home feeds for active users automatically.
**Version history:**
2.6.0 - added
| Option | Description |
| :--- | :--- |
| USERNAME | Local username whose feeds will be regenerated. |
| --all | Can be provided instead of USERNAME to refresh all local accounts' feeds. |
| --concurrency N | The number of workers to use for this task. Defaults to N=5. |
| --verbose | Print additional information while task is processing. |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl feeds clear`
Remove all home and list feeds from Redis.
**Version history:**
2.6.0 - added
## Media CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/media_cli.rb" caption="lib/mastodon/media\_cli.rb" >}}
### `tootctl media remove`
Remove locally cached copies of media attachments from other servers.
**Version history:**
2.5.0 - added
2.6.2 - show freed disk space
| Option | Description |
| :--- | :--- |
| --days | How old media attachments have to be before they are removed. Defaults to 7. |
| --concurrency N | The number of workers to use for this task. Defaults to 5. |
| --verbose | Print additional information while task is processing. |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl media remove-orphans`
Scans for files that do not belong to existing media attachments, and remove them. Please mind that some storage providers charge for the necessary API requests to list objects. Also, this operation requires iterating over every single file individually, so it will be slow.
**Version history:**
3.1.0 - added
| Option | Description |
| :--- | :--- |
| --start\_after | The Paperclip attachment key where the loop will start. Use this option if the command was interrupted before. |
| --dry\_run | Print expected results only, without performing any actions. |
### `tootctl 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.
**Version history:**
3.0.0 - added
3.0.1 - add `--force` and skip already downloaded attachments by default
| Option | Description |
| :--- | :--- |
| --account ACCT | String `username@domain` handle of the account |
| --domain DOMAIN | FQDN string |
| --status ID | Local numeric ID of the status in the database. |
| --concurrency N | The number of workers to use for this task. Defaults to 5. |
| --verbose | Print additional information while task is processing. |
| --dry\_run | Print expected results only, without performing any actions. |
| --force | Force redownload the remote resource and overwrite the local attachment. |
### `tootctl media usage`
Calculate disk space consumed by Mastodon.
**Version history:**
3.0.1 - added
### `tootctl media lookup`
Prompts for a media URL, then looks up where the media is displayed.
**Version history:**
3.1.0 - added
## Preview Cards CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/preview_cards_cli.rb" caption="lib/mastodon/preview\_cards\_cli.rb" >}}
### `tootctl preview_cards remove`
Remove local thumbnails for preview cards.
**Version history:**
3.0.0 - added
| Option | Description |
| :--- | :--- |
| --days | How old media attachments have to be before they are removed. Defaults to 180. \(NOTE: it is not recommended to delete preview cards within the last 14 days, because preview cards will not be refetched unless the link is reposted after 2 weeks from last time.\) |
| --concurrency N | The number of workers to use for this task. Defaults to 5. |
| --verbose | Print additional information while task is processing. |
| --dry\_run | Print expected results only, without performing any actions. |
| --link | Only delete link-type preview cards; leave video and photo cards untouched. |
## Search CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/search_cli.rb" caption="lib/mastodon/search\_cli.rb" >}}
### `tootctl 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.
**Version history:**
2.8.0 - added
3.0.0 - add `--processes` for parallelization
| Option | Description |
| :--- | :--- |
| --processes N | Parallelize execution of the command. Defaults to N=2. Can also specify `auto` to derive a number based on available CPUs. |
## Settings CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/settings_cli.rb" caption="lib/mastodon/settings\_cli.rb" >}}
### `tootctl settings registrations open`
Opens registrations.
**Version history:**
2.6.0 - added
### `tootctl settings registrations close`
Closes registrations.
**Version history:**
2.6.0 - added
## Statuses CLI
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/lib/mastodon/statuses_cli.rb" caption="lib/mastodon/statuses\_cli.rb" >}}
### `tootctl statuses remove`
Remove unreferenced statuses from the database, such as statuses that came from relays or from users who are no longer followed by any local accounts, and have not been replied to or otherwise interacted with.
This is a computationally heavy procedure that creates extra database indices before commencing, and removes them afterward.
**Version history:**
2.8.0 - added
| Option | Description |
| :--- | :--- |
| --days | How old statuses have to be before they are removed. Defaults to 90. |

View File

@ -0,0 +1,28 @@
---
title: Troubleshooting errors
menu:
docs:
weight: 120
parent: admin
---
## **I see an error page that says something went wrong. How do I find out whats wrong?**
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, its 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.
Each response from Mastodons 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.
## **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 Mastodons web process, because it looks like its serving outdated stylesheets and scripts. Its 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, its possible to precompile the assets on a different machine, then copy over the `public/packs` directory.
## **After an upgrade to a newer version, some requests fail and the logs show error messages about missing columns or tables. Why?**
Check that you have run `RAILS_ENV=production bin/rails db:migrate` after the upgrade, because it looks like Mastodons code is accessing a newer or older database schema. If you are using PgBouncer, make sure this one command connects directly to PostgreSQL, as PgBouncer does not support the kind of table locks that are used within migrations.
## **I am trying to run a `tootctl` or `rake`/`rails` command, but all I get is an error about uninitialized constants. Whats wrong?**
Check that you are specifying the correct environment with `RAILS_ENV=production` before the command. By default, the environment is assumed to be development, so the code tries to load development-related gems. However, in production environments, we avoid installing those gems, and thats where the error comes from.

View File

@ -1,57 +1,55 @@
---
title: Upgrading to a new release
description: How to upgrade Mastodon to a newer version
menu:
docs:
parent: administration
weight: 5
weight: 70
parent: admin
---
{{< hint style="info" >}}
When a new version of Mastodon comes out, it appears on the [GitHub releases page](https://github.com/tootsuite/mastodon/releases). Please mind that running unreleased code from the `master` branch, while possible, is not recommended.
{{< /hint >}}
Mastodon releases correspond to git tags. First, switch to the `mastodon` user:
```sh
```bash
su - mastodon
```
And navigate to the Mastodon root directory:
```sh
cd /home/mastodon/live
```
Download the releasess code, assuming that the version is called `v2.5.0`:
Download the releases's code, assuming that the version is called `v2.5.0`:
```sh
```bash
git fetch --tags
git checkout v2.5.0
```
The release page contains a changelog, and below it, upgrade instructions. This is where you would execute them, for example, if the release mentions that you need to re-compile assets, you would execute:
**The release page contains a changelog, and below it, upgrade instructions**. This is where you would execute them, for example, if the release mentions that you need to re-compile assets, you would execute:
```sh
```bash
RAILS_ENV=production bundle exec rails assets:precompile
```
After you have executed all special release-specific instructions, the last thing remaining is restarting Mastodon. *Usually* the streaming API is not updated, and therefore does not require a restart. Restarting the streaming API can lead to an unusually high load on the server, so it is advised to avoid it if possible.
After you have executed all special release-specific instructions, the last thing remaining is restarting Mastodon. _Usually_ the streaming API is not updated, and therefore does not require a restart. Restarting the streaming API can lead to an unusually high load on the server, so it is advised to avoid it if possible.
Switch back to root:
```sh
```bash
exit
```
You would restart Sidekiq:
```sh
```bash
systemctl restart mastodon-sidekiq
```
And you would reload the web process to avoid downtime:
```sh
```bash
systemctl reload mastodon-web
```
**That's all!** You're running the new version of Mastodon now.
**Thats all!** Youre running the new version of Mastodon now.

View File

@ -1,216 +0,0 @@
---
title: Configuration
description: Overview of Mastodon's configuration options
menu:
docs:
parent: administration
weight: 2
---
Mastodon uses environment variables as its configuration.
For convenience, it can read them from a flat file called `.env.production` in the Mastodon directory, 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.
## Basic
### Federation
- `LOCAL_DOMAIN`
- `WEB_DOMAIN`
- `ALTERNATE_DOMAINS`
### Secrets
- `SECRET_KEY_BASE`
- `OTP_SECRET`
- `VAPID_PRIVATE_KEY`
- `VAPID_PUBLIC_KEY`
### Deployment
- `RAILS_ENV`
- `RAILS_SERVE_STATIC_FILES`
- `RAILS_LOG_LEVEL`
- `TRUSTED_PROXY_IP`
- `SOCKET`
- `PORT`
- `NODE_ENV`
- `BIND`
### Scaling options
- `WEB_CONCURRENCY`
- `MAX_THREADS`
- `PREPARED_STATEMENTS`
- `STREAMING_API_BASE_URL`
- `STREAMING_CLUSTER_NUM`
## Database connections
### PostgreSQL
- `DB_HOST`
- `DB_USER`
- `DB_NAME`
- `DB_PASS`
- `DB_PORT`
- `DATABASE_URL`
### Redis
- `REDIS_HOST`
- `REDIS_PORT`
- `REDIS_URL`
- `REDIS_NAMESPACE`
- `CACHE_REDIS_HOST`
- `CACHE_REDIS_PORT`
- `CACHE_REDIS_URL`
- `CACHE_REDIS_NAMESPACE`
### ElasticSearch
- `ES_ENABLED`
- `ES_HOST`
- `ES_PORT`
- `ES_PREFIX`
### StatsD
- `STATSD_ADDR`
- `STATSD_NAMESPACE`
## Limits
- `SINGLE_USER_MODE`
- `EMAIL_DOMAIN_WHITELIST`
- `DEFAULT_LOCALE`
- `MAX_SESSION_ACTIVATIONS`
- `USER_ACTIVE_DAYS`
## E-mail
- `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_TLS`
## File storage
- `CDN_HOST`
- `S3_ALIAS_HOST`
### Local file storage
- `PAPERCLIP_ROOT_PATH`
- `PAPERCLIP_ROOT_URL`
### Amazon S3 and compatible
- `S3_ENABLED`
- `S3_BUCKET`
- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- `S3_REGION`
- `S3_PROTOCOL`
- `S3_HOSTNAME`
- `S3_ENDPOINT`
- `S3_SIGNATURE_VERSION`
### 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
- `OAUTH_REDIRECT_AT_SIGN_IN`
### LDAP
- `LDAP_ENABLED`
- `LDAP_HOST`
- `LDAP_PORT`
- `LDAP_METHOD`
- `LDAP_BASE`
- `LDAP_BIND_DN`
- `LDAP_PASSWORD`
- `LDAP_UID`
- `LDAP_SEARCH_FILTER`
### PAM
- `PAM_ENABLED`
- `PAM_EMAIL_DOMAIN`
- `PAM_DEFAULT_SERVICE`
- `PAM_CONTROLLED_SERVICE`
### CAS
- `CAS_ENABLED`
- `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`
### 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
- `http_proxy`
- `ALLOW_ACCESS_TO_HIDDEN_SERVICE`
## Other
- `SKIP_POST_DEPLOYMENT_MIGRATIONS`

View File

@ -1,333 +0,0 @@
---
title: Installation
description: How to install Mastodon on an Ubuntu 18.04 server
menu:
docs:
parent: administration
weight: 1
---
<img src="/setup.png" alt="" style="margin: 0; box-shadow: none">
## Basic server setup (optional)
If you are setting up a fresh machine, it is recommended that you secure it first. Assuming that you are running **Ubuntu 18.04**:
### 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.
Edit `/etc/ssh/sshd_config` and find `PasswordAuthentication`. Make sure it's uncommented and set to `no`. If you made any changes, restart sshd:
```sh
systemctl restart ssh
```
### Update system packages
```sh
apt update && apt upgrade -y
```
### Install fail2ban so it blocks repeated login attempts
```sh
apt install fail2ban
```
Edit `/etc/fail2ban/jail.local` and put this inside:
```ini
[DEFAULT]
destemail = your@email.here
sendername = Fail2Ban
[sshd]
enabled = true
port = 22
[sshd-ddos]
enabled = true
port = 22
```
Finally restart fail2ban:
```sh
systemctl restart fail2ban
```
### Install a firewall and only whitelist SSH, HTTP and HTTPS ports
First, install iptables-persistent. During installation it will ask you if you want to keep current rules--decline.
```sh
apt install -y iptables-persistent
```
Edit `/etc/iptables/rules.v4` and put this inside:
```
*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# 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
# Allow SSH connections
# The -dport number should be the same port number you set in sshd_config
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Reject all other inbound - default deny unless explicitly allowed policy
-A INPUT -j REJECT
-A FORWARD -j REJECT
COMMIT
```
With iptables-persistent, that configuration will be loaded at boot time. But since we are not rebooting right now, we need to load it manually for the first time:
```sh
iptables-restore < /etc/iptables/rules.v4
```
## Pre-requisites
- A machine running **Ubuntu 18.04** that you have root access to
- A **domain name** (or a subdomain) for the Mastodon server, e.g. `example.com`
- An e-mail delivery service or other **SMTP server**
You will be running the commands as root. If you aren't already root, switch to root:
```sh
sudo -i
```
### System repositories
Make sure curl is installed first:
```sh
apt install -y curl
```
#### Node.js
```sh
curl -sL https://deb.nodesource.com/setup_8.x | bash -
```
#### Yarn
```sh
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
```
### System packages
```sh
apt update
apt install -y \
imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core \
g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf \
bison build-essential libssl-dev libyaml-dev libreadline6-dev \
zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev \
nginx redis-server redis-tools postgresql postgresql-contrib \
certbot python-certbot-nginx yarn libidn11-dev libicu-dev libjemalloc-dev
```
### 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:
```sh
adduser --disabled-login mastodon
```
We can then switch to the user:
```sh
su - mastodon
```
And proceed to install rbenv and rbenv-build:
```sh
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
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
```
Once this is done, we can install the correct Ruby version:
```sh
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.6.1
rbenv global 2.6.1
```
Default gem version shipped with ruby_2.6.0 is incompatible with latest bundler, so we need to update gem:
```
gem update --system
```
We'll also need to install bundler:
```sh
gem install bundler --no-document
```
Return to the root user:
```sh
exit
```
## Setup
### Setting up PostgreSQL
#### 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/9.6/main/postgresql.conf` before restarting PostgreSQL with `systemctl restart postgresql`
#### Creating a user
You will need to create a PostgreSQL user that Mastodon could use. It is easiest to go with "ident" authentication in a simple setup, i.e. the PostgreSQL user does not have a separate password and can be used by the Linux user with the same username.
Open the prompt:
```sh
sudo -u postgres psql
```
In the prompt, execute:
```
CREATE USER mastodon CREATEDB;
\q
```
Done!
### Setting up Mastodon
It is time to download the Mastodon code. Switch to the mastodon user:
```sh
su - mastodon
```
#### Checking out the code
Use git to download the latest stable release of Mastodon:
```sh
git clone https://github.com/tootsuite/mastodon.git live && cd live
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
```
#### Installing the last dependencies
Now to install Ruby and JavaScript dependencies:
```sh
bundle install \
-j$(getconf _NPROCESSORS_ONLN) \
--deployment --without development test
yarn install --pure-lockfile
```
#### Generating a configuration
Run the interactive setup wizard:
```sh
RAILS_ENV=production bundle exec rake mastodon:setup
```
This will:
- Create a configuration file
- Run asset precompilation
- Create the database schema
The configuration file is saved as `.env.production`. You can review and edit it to your liking. Refer to the [documentation on configuration]({{< relref "configuration.md" >}}).
You're done with the mastodon user for now, so switch back to root:
```sh
exit
```
### Setting up nginx
Copy the configuration template for nginx from the Mastodon directory:
```sh
cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
```
Then edit `/etc/nginx/sites-available/mastodon` to replace `example.com` with your own domain name, and make any other adjustments you might need.
Reload nginx for the changes to take effect:
```sh
systemctl reload nginx
```
### Acquiring a SSL certificate
We'll use Let's Encrypt to get a free SSL certificate:
```sh
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.
### Setting up systemd services
Copy the systemd service templates from the Mastodon directory:
```sh
cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
```
Then edit the files to make sure the username and paths are correct:
- `/etc/systemd/system/mastodon-web.service`
- `/etc/systemd/system/mastodon-sidekiq.service`
- `/etc/systemd/system/mastodon-streaming.service`
Finally, start and enable the new systemd services:
```sh
systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
systemctl enable mastodon-*
```
They will now automatically start at boot time.
**Hurray! This is it. You can visit your domain in the browser now!**

View File

@ -1,103 +0,0 @@
---
title: Migrating servers
description: How to migrate a Mastodon instance to a new server
menu:
docs:
parent: administration
weight: 6
---
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.
**Note:** this guide was written with Ubuntu Server in mind; your mileage may vary for other setups.
Basic steps
----
1. Set up a new Mastodon server using the [Production Guide](/administration/installation/) (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.
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.
11. Enjoy your new server!
Detailed steps
----
### What data needs to be migrated
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 `~/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 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)
### Dump and load Postgres
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)).
Run this as the `mastodon` user on your old system:
```bash
pg_dump -Fc mastodon_production -f backup.dump
```
Copy the `backup.dump` file over, using `rsync` or `scp`. Then on the new system,
create an empty database as the `mastodon` user:
```bash
createdb -T template0 mastodon_production
```
Then import it:
```bash
pg_restore -U mastodon -n public --no-owner --role=mastodon \
-d mastodon_production backup.dump
```
(Note that if the username is not `mastodon` on the new server, you should change the
`-U` AND `--role` values above. It's okay if the username is different between the two servers.)
### Copy files
This will probably take some time, and you'll want to avoid re-copying unnecessarily, so using `rsync` is recommended.
On your old machine, as the `mastodon` user, run:
```bash
rsync -avz ~/live/public/system/ mastodon@example.com:~/live/public/system/
```
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.
### During migration
You can edit the `~/live/public/500.html` page on the old machine if you want to show a nice error message to
let existing users know that a migration is in progress.
You'll probably also want to set the DNS TTL to something small (30-60 minutes) about a day in advance, so
that DNS can propagate quickly once you point it to the new IP address.
### After migrating
You can check [whatsmydns.net](http://whatsmydns.net/) to see the progress of DNS propagation.
To jumpstart the process, you can always edit your own `/etc/hosts` file to point to your new server so
you can start playing around with it early.

View File

@ -1,106 +0,0 @@
---
title: Post-installation steps
description: What to do after the installation of Mastodon is complete
menu:
docs:
parent: administration
weight: 3
---
## Using the command-line interface
The command-line interface of Mastodon is an executable file called `tootctl` residing in the `bin` directory within the Mastodon root directory. You must specify which environment you intend to use whenever you execute it by specifying the `RAILS_ENV` environment variable. Unless you are a developer working on a local machine, you need to use `RAILS_ENV=production`. If you are sure that you will never need another environment (for development, testing, or staging), you can add it to your `.bashrc` file for convenience, e.g.:
```bash
echo "export RAILS_ENV=production" >> ~/.bashrc
```
If so, you won't need to specify it each time inline. Otherwise, calls to `tootctl` will usually go like this, assuming that the Mastodon code is checked out in `/home/mastodon/live`:
```bash
cd /home/mastodon/live
RAILS_ENV=production bin/tootctl help
```
## Creating an admin account
### In the browser
After signing up in the browser, you will need to use the command line to give your newly created account admin privileges. Assuming your username is `alice`:
```bash
RAILS_ENV=production bin/tootctl accounts modify alice --role admin
```
### From the command line
You can create a new account using the command-line interface.
```bash
RAILS_ENV=production bin/tootctl accounts create \
alice \
--email alice@example.com \
--confirmed \
--role admin
```
A randomly generated password will be shown in the terminal.
## Filling in server information
After logging in, navigate to the **Site settings** page. While there are no technical requirements for filling in this information, it is considered crucial for operating a server for humans.
|Setting|Meaning|
|-------|-------|
|Contact username|Your username so people know who owns the server|
|Business e-mail|An e-mail address so people locked out of their accounts, or people without accounts, can contact you|
|Instance description|Why did you start this server? Who is it for? What makes it different?|
|Custom extended information|You can put all sorts of information in here but a **code of conduct** is recommended|
After you fill these in, simply hit "Save changes".
## Setting up regular backups (optional, but not really)
For any real-world use, you should make sure to regularly backup your Mastodon server.
### Overview
Things that need to be backed up in order of importance:
1. PostgreSQL database
2. Application secrets from the `.env.production` file or equivalent
3. User-uploaded files
4. Redis database
### Failure modes
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.
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.
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 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.
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
Application secrets are the easiest to backup, since they never change. You only need to store `.env.production` somewhere safe.
### Backing up PostgreSQL
PostgreSQL is at risk of data corruption from power cuts, hard disk drive failure, and botched schema migrations. For that reason, occassionally making a backup with `pg_dump` or `pg_dumpall` is recommended.
For high-availability setups, it is possible to use hot streaming replication to have a second PostgreSQL server with always up-to-date data, ready to be switched over to if the other server goes down.
### Backing up user-uploaded files
If you are using an external object storage provider such as Amazon S3, Google Cloud or Wasabi, then you don't need to worry about backing those up. The respective companies are responsible for handling hardware failures.
If you are using local file storage, then it's up to you to make copies of the sizeable `public/system` directory, where uploaded files are stored by default.
### Backing up Redis
Backing up Redis is easy. Redis regularly writes to `/var/lib/redis/dump.rdb` which is the only file you need to make a copy of.

View File

@ -1,265 +0,0 @@
---
title: Scaling up
description: How to scale Mastodon horizontally to handle more requests
menu:
docs:
parent: administration
weight: 4
---
## Managing concurrency
Mastodon has three types of processes:
- Web (Puma)
- Streaming API
- Background processing (Sidekiq)
### Web (Puma)
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
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.
These values affect how many HTTP requests can be served at the same time.
In terms of throughput, more processes are better than more threads.
### Streaming API
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
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.
### Background processing (Sidekiq)
Many tasks in Mastodon are delegated to background processing to ensure the HTTP requests are fast, and to prevent HTTP request aborts from affecting the execution of those tasks. Sidekiq is a single process, with a configurable number of threads.
#### Number of threads
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.:
```sh
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.
#### 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`|Fetching information from other servers|
The default queues and their priorities are stored in `config/sidekiq.yml`, but can be overridden by the command-line invocation of Sidekiq, e.g.:
```sh
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.
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.
## Transaction pooling with pgBouncer
### Why you might need PgBouncer
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.
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.
### Installing PgBouncer
On Debian and Ubuntu:
sudo apt install pgbouncer
### Configuring PgBouncer
#### Setting a password
First off, if your `mastodon` user in Postgres is set up wthout a password, you will need to set a password.
Here's how you might reset the password:
psql -p 5432 -U mastodon mastodon_production -w
Then (obviously, use a different password than the word "password"):
ALTER USER mastodon WITH PASSWORD 'password';
Then `\q` to quit.
#### Configuring userlist.txt
Edit `/etc/pgbouncer/userlist.txt`
As long as you specify a user/password in pgbouncer.ini later, the values in userlist.txt do *not* have to correspond to real PostgreSQL roles. You can arbitrarily define users and passwords, but you can reuse the "real" credentials for simplicity's sake. Add the `mastodon` user to the `userlist.txt`:
"mastodon" "md5d75bb2be2d7086c6148944261a00f605"
Here we're using the md5 scheme, where the md5 password is just the md5sum of `password + username` with the string `md5` prepended. For instance, to derive the hash for user `mastodon` with password `password`, you can do:
```bash
# ubuntu, debian, etc.
echo -n "passwordmastodon" | md5sum
# macOS, openBSD, etc.
md5 -s "passwordmastodon"
```
Then just add `md5` to the beginning of that.
You'll also want to create a `pgbouncer` admin user to log in to the PgBouncer admin database. So here's a sample `userlist.txt`:
```
"mastodon" "md5d75bb2be2d7086c6148944261a00f605"
"pgbouncer" "md5a45753afaca0db833a6f7c7b2864b9d9"
```
In both cases the password is just `password`.
#### Configuring 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:
```ini
[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:
```ini
listen_addr = 127.0.0.1
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:
```ini
admin_users = pgbouncer
```
**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
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 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.)
The defaults are fine to start, and you can always increase them later:
```ini
max_client_conn = 100
default_pool_size = 20
```
Don't forget to reload or restart pgbouncer after making your changes:
sudo systemctl reload pgbouncer
#### Debugging that it all works
You should be able to connect to PgBouncer just like you would with Postgres:
psql -p 6432 -U mastodon mastodon_production
And then use your password to log in.
You can also check the PgBouncer logs like so:
tail -f /var/log/postgresql/pgbouncer.log
#### Configuring Mastodon to talk to PgBouncer
In your `.env.production` file, first off make sure that this is set:
```bash
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:
```bash
DB_HOST=localhost
DB_USER=mastodon
DB_NAME=mastodon_production
DB_PASS=password
DB_PORT=6432
```
> **Gotcha:** 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)
#### Administering PgBouncer
The easiest way to reboot is:
sudo systemctl restart pgbouncer
But if you've set up a PgBouncer admin user, you can also connect as the admin:
psql -p 6432 -U pgbouncer pgbouncer
And then do:
RELOAD;
Then use `\q` to quit.
## Separate Redis for cache
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.
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)
## 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:
- 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.
You will have to edit the `config/database.yml` file and replace the `production` section as follows:
```yml
production:
<<: *default
adapter: postgresql_makara
prepared_statements: false
makara:
id: postgres
sticky: true
connections:
- role: master
blacklist_duration: 0
url: postgresql://db_user:db_password@db_host:db_port/db_name
- role: slave
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).

View File

@ -1,28 +0,0 @@
---
title: Troubleshooting
description: How to figure out what's wrong with your Mastodon installation
menu:
docs:
parent: administration
weight: 99
---
**I see an error page that says something went wrong. How do I find out what's wrong?**
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.
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.
**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.
**After an upgrade to a newer version, some requests fail and the logs show error messages about missing columns or tables. Why?**
Check that you have run `RAILS_ENV=production bin/rails db:migrate` after the upgrade, because it looks like Mastodon's code is accessing a newer or older database schema. If you are using PgBouncer, make sure this one command connects directly to PostgreSQL, as PgBouncer does not support the kind of table locks that are used within migrations.
**I am trying to run a `tootctl` or `rake`/`rails` command, but all I get is an error about uninitialized constants. What's wrong?**
Check that you are specifying the correct environment with `RAILS_ENV=production` before the command. By default, the environment is assumed to be development, so the code tries to load development-related gems. However, in production environments, we avoid installing those gems, and that's where the error comes from.

View File

@ -1,48 +0,0 @@
---
title: Authentication
description: How to authenticate with OAuth 2 on Mastodon
menu:
docs:
parent: api
weight: 1
---
Mastodon is federated, therefore you can't be expected to manually register your application on all potential servers your users might want to login on. For this reason, there is an open app registration API, so obtaining OAuth 2 credentials for OAuth 2 authorization can be automated.
Make sure that you allow your users to specify the domain they want to connect to before login. Use that domain to acquire a client id/secret for OAuth 2 and then proceed with normal OAuth 2 also using that domain to build the URLs.
Mastodon supports the following OAuth 2 flows:
- **Authorization code flow**: For end-users
- **Password grant flow**: For bots and other single-user applications
- **Client credentials flow**: For applications that do not act on behalf of users
## OAuth 2 endpoints
The following descriptions are taken from the [Doorkeeper documentation](https://github.com/doorkeeper-gem/doorkeeper/wiki/API-endpoint-descriptions-and-examples). Mastodon uses Doorkeeper to implement OAuth 2.
### GET /oauth/authorize
Redirect here with `response_type=code`, `client_id`, `client_secret`, `redirect_uri`, `scope`, and optional `state`. Displays an authorization form to the user. If approved, it will create and return an authorization code, then redirect to the desired `redirect_uri`, or show the authorization code if `urn:ietf:wg:oauth:2.0:oob` was requested.
### POST /oauth/token
Post here with `authorization_code` for authorization code grant type or `username` and `password` for password grant type. Returns an access token. This corresponds to the token endpoint, section 3.2 of the OAuth 2 RFC.
### POST /oauth/revoke
Post here with client credentials (in basic auth or in params `client_id` and `client_secret`) to revoke an access token. This corresponds to the token endpoint, using the OAuth 2.0 Token Revocation RFC (RFC 7009).
## Example authorization code flow
1. Get `client_id` and `client_secret` from your local cache. If you don't have the two, you need to [register the application]({{< relref "api/rest/apps.md#post-api-v1-apps" >}}). Store `client_id` and `client_secret` in your local cache for next time. We actually don't need the `id` returned from this call.
1. Tell the user to visit `/oauth/authorize` with parameters `scope`, `response_type=code`, `redirect_uri`, your `client_id`, and optionally a randomly-generated `state` parameter. The user clicks on the URL and gets shown a page asking them to authorize your app for the scopes you requested. If the user clicks on the right button, they are redirected back to your `redirect_uri` with a `code` param in the query string. That is the authorization code. If you provided a `state` value in the previous step, that will be passed along as well.
1. Send a POST request to `/oauth/token` with the parameters `client_id`, `client_secret`, `grant_type=authorization_code`, `code`, `redirect_uri`. Save the `access_token` you get back in your local cache. Note that an authorization code can only be used once. If it has been used already, you need to repeat step two to get a new one.
Once you have the access token, add the HTTP header `Authorization: Bearer ...` to any API call.
## Common gotchas
- The OAuth param name is `scope`, but when registering the application using Mastodon's REST API, the param name is `scopes`. The OAuth param can be a subset of the scopes you registered initially, but cannot include anything that wasn't in the original set.
- The OAuth param name is `redirect_uri`, but when registering the application using Mastodon's REST API, the param name is `redirect_uris`. The latter can actually consist of multiple allowed URIs, separated by newlines.
- The `redirect_uri` in all OAuth requests must either be the same as the one registered with the application, or one of them, if you registered multiple URIs separated by newlines with the application.

View File

@ -1,380 +0,0 @@
---
title: Entities
description: Overview of entities returned from Mastodon's REST API
menu:
docs:
parent: api
weight: 3
---
- All IDs are encoded as string representations of integers.
- IDs can be sorted first by size, and then lexically, to produce a chronological ordering of resources.
- All datetimes are in ISO 8601 format
- All HTML strings are sanitized by the server
- All language codes are in ISO 6391 format
## Account
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|0.1.0|
| `username` | String |{{< no >}}|0.1.0|
| `acct` | String |{{< no >}}|0.1.0|
| `display_name` | String |{{< no >}}|0.1.0|
| `locked` | Boolean |{{< no >}}|0.1.0|
| `created_at` | String (Datetime) |{{< no >}}|0.1.0|
| `followers_count` | Number |{{< no >}}|0.1.0|
| `following_count` | Number |{{< no >}}|0.1.0|
| `statuses_count` | Number |{{< no >}}|0.1.0|
| `note` | String |{{< no >}}|0.1.0|
| `url` | String (URL) |{{< no >}}|0.1.0|
| `avatar` | String (URL) |{{< no >}}|0.1.0|
| `avatar_static` | String (URL) |{{< no >}}|1.1.2|
| `header` | String (URL) |{{< no >}}|0.1.0|
| `header_static` | String (URL) |{{< no >}}|1.1.2|
| `emojis` | Array of [Emoji](#emoji) |{{< no >}}|2.4.0|
| `moved` | [Account](#account) |{{< yes >}}|2.1.0|
| `fields` | Array of [Hash](#field) |{{< yes >}}|2.4.0|
| `bot` | Boolean |{{< yes >}}|2.4.0|
### Field
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `name` | String |{{< no >}}|2.4.0|
| `value` | String (HTML) |{{< no >}}|2.4.0|
| `verified_at` | String (Datetime) |{{< yes >}}|2.6.0|
### Source
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `privacy` | String |{{< yes >}}|1.5.0|
| `sensitive` | Boolean |{{< yes >}}|1.5.0|
| `language` | String (ISO6391) |{{< yes >}}|2.4.2|
| `note` | String |{{< no >}}|1.5.0|
| `fields` | Array of Hash |{{< no >}}|2.4.0|
### Token
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `access_token` | String |{{< no >}}|0.1.0|
| `token_type` | String |{{< no >}}|0.1.0|
| `scope` | String |{{< no >}}|0.1.0|
| `created_at` | Number |{{< no >}}|0.1.0|
## Application
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `name` | String |{{< no >}}|0.9.9|
| `website` | String (URL) |{{< yes >}}|0.9.9|
## Attachment
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|0.6.0|
| `type` | [String (Enum)](#type) |{{< no >}}|0.6.0|
| `url` | String (URL) |{{< no >}}|0.6.0|
| `remote_url` | String (URL) |{{< yes >}}|0.6.0|
| `preview_url` | String (URL) |{{< no >}}|0.6.0|
| `text_url` | String (URL) |{{< yes >}}|0.6.0|
| `meta` | [Hash](#meta) |{{< yes >}}|1.5.0|
| `description` | String |{{< yes >}}|2.0.0|
### Type
- `unknown`
- `image`
- `gifv`
- `video`
### Meta
May contain subtrees `small` and `original`.
Images may contain `width`, `height`, `size`, `aspect`, while videos (including GIFV) may contain `width`, `height`, `frame_rate`, `duration` and `bitrate`.
There may be another top-level object, `focus` with the coordinates `x` and `y`. These coordinates can be used for smart thumbnail cropping, [see this for reference](https://github.com/jonom/jquery-focuspoint#1-calculate-your-images-focus-point).
## Card
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `url` | String (URL) |{{< no >}}|1.0.0|
| `title` | String |{{< no >}}|1.0.0|
| `description` | String |{{< no >}}|1.0.0|
| `image` | String (URL) |{{< yes >}}|1.0.0|
| `type` | [String (Enum)](#type-1) |{{< no >}}|1.3.0|
| `author_name` | String |{{< yes >}}|1.3.0|
| `author_url` | String (URL) |{{< yes >}}|1.3.0|
| `provider_name` | String |{{< yes >}}|1.3.0|
| `provider_url` | String (URL) |{{< yes >}}|1.3.0|
| `html` | String (HTML) |{{< yes >}}|1.3.0|
| `width` | Number |{{< yes >}}|1.3.0|
| `height` | Number |{{< yes >}}|1.3.0|
### Type
- `link`
- `photo`
- `video`
- `rich`
## Context
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `ancestors` | Array of [Status](#status) |{{< no >}}|0.6.0|
| `descendants` | Array of [Status](#status) |{{< no >}}|0.6.0|
## Emoji
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `shortcode` | String |{{< no >}}|2.0.0|
| `static_url` | String (URL) |{{< no >}}|2.0.0|
| `url` | String (URL) |{{< no >}}|2.0.0|
| `visible_in_picker` | Boolean |{{< no >}}|2.1.0|
## Error
The most important part of an error response is the HTTP status code. Standard semantics are followed. The body of an error is a JSON object with this structure:
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `error` | String |{{< no >}}|0.6.0|
## Filter
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|2.4.3|
| `phrase` | String |{{< no >}}|2.4.3|
| `context` | Array of [String (Enum)](#context-1) |{{< no >}}|2.4.3|
| `expires_at` | String (Datetime) |{{< yes >}}|2.4.3|
| `irreversible` | Boolean |{{< no >}}|2.4.3|
| `whole_word` | Boolean |{{< no >}}|2.4.3|
### Context
- `home`
- `notifications`
- `public`
- `thread`
### Implementation notes
If `whole_word` is true , client app should do:
- Define 'word constituent character' for your app. In the official implementation, it's `[A-Za-z0-9_]` in JavaScript, and `[[:word:]]` in Ruby. In Ruby case it's 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.
Please check `app/javascript/mastodon/selectors/index.js` and `app/lib/feed_manager.rb` in the Mastodon source code for more details.
## Instance
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `uri` | String |{{< no >}}|1.1.0|
| `title` | String |{{< no >}}|1.1.0|
| `description` | String |{{< no >}}|1.1.0|
| `email` | String |{{< no >}}|1.1.0|
| `version` | String |{{< no >}}|1.3.0|#
| `thumbnail` | String (URL) |{{< yes >}}|1.6.1|
| `urls` | [Hash](#urls) |{{< no >}}|1.4.2|
| `stats` | [Hash](#stats) |{{< no >}}|1.6.0|
| `languages` | Array of String (ISO 639, Part 1-5) |{{< no >}}|2.3.0|
| `contact_account` | [Account](#account) |{{< yes >}}|2.3.0|
### URLs
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
|`streaming_api`| String (URL) |{{< no >}}|1.4.2|
### Stats
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
|`user_count`| Number |{{< no >}}|1.6.0|
|`status_count`| Number |{{< no >}}|1.6.0|
|`domain_count`| Number |{{< no >}}|1.6.0|
## List
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|2.1.0|
| `title` | String |{{< no >}}|2.1.0|
## Mention
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `url` | String (URL) |{{< no >}}|0.6.0|
| `username` | String |{{< no >}}|0.6.0|
| `acct` | String |{{< no >}}|0.6.0|
| `id` | String |{{< no >}}|0.6.0|
## Notification
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|0.9.9|
| `type` | [String (Enum)](#type-2) |{{< no >}}|0.9.9|
| `created_at` | String (Datetime) |{{< no >}}|0.9.9|
| `account` | [Account](#account) |{{< no >}}|0.9.9|
| `status` | [Status](#status) |{{< yes >}}|0.9.9|
### Type
- `follow`
- `mention`
- `reblog`
- `favourite`
- `poll`
## Poll
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|2.8.0|
| `expires_at` | String (Datetime) |{{< yes >}}|2.8.0|
| `expired` | Boolean |{{< no >}}|2.8.0|
| `multiple` | Boolean |{{< no >}}|2.8.0|
| `votes_count` | Number |{{< no >}}|2.8.0|
| `options` | Array of [Poll option](#poll-option) |{{< no >}}|2.8.0|
| `voted` | Boolean |{{< yes >}}|2.8.0|
### Poll option
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `title` | String |{{< no >}}|2.8.0|
| `votes_count` | Number |{{< yes >}}|2.8.0|
## Push subscription
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|2.4.0|
| `endpoint` | String (URL) |{{< no >}}|2.4.0|
| `server_key` | String |{{< no >}}|2.4.0|
| `alerts` | [Hash](#alerts) |{{< no >}}|2.4.0|
### Alerts
???
## Relationship
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|0.6.0|
| `following` | Boolean |{{< no >}}|0.6.0|
| `followed_by` | Boolean |{{< no >}}|0.6.0|
| `blocking` | Boolean |{{< no >}}|0.6.0|
| `muting` | Boolean |{{< no >}}|1.1.0|
| `muting_notifications` | Boolean |{{< no >}}|2.1.0|
| `requested` | Boolean |{{< no >}}|0.9.9|
| `domain_blocking` | Boolean |{{< no >}}|1.4.0|
| `showing_reblogs` | Boolean |{{< no >}}|2.1.0|
| `endorsed` | Boolean |{{< no >}}|2.5.0|
## Results
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `accounts` | Array of [Account](#account) |{{< no >}}|1.1.0|
| `statuses` | Array of [Status](#status) |{{< no >}}|1.1.0|
| `hashtags` | Array of [Tag](#tag) |{{< no >}}|1.1.0|
## Status
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|0.1.0|
| `uri` | String |{{< no >}}|0.1.0|
| `url` | String (URL) |{{< yes >}}|0.1.0|
| `account` | [Account](#account) |{{< no >}}|0.1.0|
| `in_reply_to_id` | String |{{< yes >}}|0.1.0|
| `in_reply_to_account_id` | String |{{< yes >}}|1.0.0|
| `reblog` | [Status](#status) |{{< yes >}}|0.1.0|
| `content` | String (HTML) |{{< no >}}|0.1.0|
| `created_at` | String (Datetime) |{{< no >}}|0.1.0|
| `emojis` | Array of [Emoji](#emoji) |{{< no >}}|2.0.0|
| `replies_count` | Number |{{< no >}}|2.5.0|
| `reblogs_count` | Number |{{< no >}}|0.1.0|
| `favourites_count` | Number |{{< no >}}|0.1.0|
| `reblogged` | Boolean |{{< yes >}}|0.1.0|
| `favourited` | Boolean |{{< yes >}}|0.1.0|
| `muted` | Boolean |{{< yes >}}|1.4.0|
| `sensitive` | Boolean |{{< no >}}|0.9.9|
| `spoiler_text` | String |{{< no >}}|1.0.0|
| `visibility` | [String (Enum)](#visibility) |{{< no >}}|0.9.9|
| `media_attachments` | Array of [Attachment](#attachment) |{{< no >}}|0.6.0|
| `mentions` | Array of [Mention](#mention) |{{< no >}}|0.6.0|
| `tags` | Array of [Tag](#tag) |{{< no >}}|0.9.0|
| `card` | [Card](#card) |{{< yes >}}|2.6.0|
| `poll` | [Poll](#poll) |{{< yes >}}|2.8.0|
| `application` | [Application](#application) |{{< no >}}|0.9.9|
| `language` | String (ISO6391) |{{< yes >}}|1.4.0|
| `pinned` | Boolean |{{< yes >}}|1.6.0|
### Visibility
- `public`
- `unlisted`
- `private`
- `direct`
## ScheduledStatus
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|2.7.0|
| `scheduled_at` | String (Datetime) |{{< no >}}|2.7.0|
| `params` | [StatusParams](#statusparams) |{{< no >}}|2.7.0|
| `media_attachments` | Array of [Attachment](#attachment) |{{< no >}}|2.7.0|
### StatusParams
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `text` | String |{{< no >}}|2.7.0|
| `in_reply_to_id` | String |{{< yes >}}|2.7.0|
| `media_ids` | Array of String |{{< yes >}}|2.7.0|
| `sensitive` | Boolean |{{< yes >}}|2.7.0|
| `spoiler_text` | String |{{< yes >}}|2.7.0|
| `visibility` | [String (Enum)](#visibility) |{{< no >}}|2.7.0|
| `scheduled_at` | String (Datetime) |{{< yes >}}|2.7.0|
| `application_id` | String |{{< no >}}|2.7.0|
## Tag
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `name` | String |{{< no >}}|0.9.0|
| `url` | String (URL) |{{< no >}}|0.9.0|
| `history` | Array of [History](#history) |{{< yes >}}|2.4.1|
### History
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `day` | String (UNIX timestamp) |{{< no >}}|2.4.1|
| `uses` | Number |{{< no >}}|2.4.1|
| `accounts` | Number |{{< no >}}|2.4.1|
## Conversation
|Attribute|Type|Nullable|Added in|
|---------|-----------|:------:|:------:|
| `id` | String |{{< no >}}|2.6.0|
| `accounts` | Array of [Account](#account) |{{< no >}}|2.6.0|
| `last_status` | [Status](#status) |{{< yes >}}|2.6.0|
| `unread` | Boolean |{{< no >}}|2.6.0|

View File

@ -1,52 +0,0 @@
---
title: Guidelines
description: Guidelines that app developers for Mastodon should follow
menu:
docs:
parent: api
weight: -1
---
## Login
**The user must be able to login to any Mastodon server from the app**. This means you must ask for either the full handle, or server domain, and use the app registrations API to dynamically obtain OAuth2 credentials.
## Usernames
**Decentralization must be transparent to the user**. It should be possible to see that a given user is from another server, by e.g. displaying their `acct` (username and domain) somewhere.
## Formatting
Plain text is not available for content from remote servers, and plain text syntax rules may vary wildly between Mastodon and other fediverse applications. For certain attributes, such as the content of statuses, **Mastodon provides sanitized HTML**.
### HTML tags
You may expect these tags to appear in the content: `<p>`, `<br>`, `<span>`, `<a>`
### Mentions and hashtags
Mentions and hashtags are `<a>` tags. To give those links 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, which can be matched to a particular tag.
### Custom emoji
Custom emoji remain in their plain text shortcode form. Metadata about the determined custom emoji is included with the status, and the shortcodes must be matched against the text to display the images.
### Other links
Links in Mastodon are not shortened using URL shorteners. However, 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
<a href="https://example.com/page/that/is/very/long">
<span class="invisible">https://</span>
<span class="ellipsis">example.com/page</span>
<span class="invisible">/that/is/very/long</span>
</a>
```
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
Clients must do their own text filtering based on filters returned from the API. The server will apply `irreversible` filters for home and notifications context, but anything else is still up to the client to filter!
Expired filters are not deleted by the server. They should no longer be applied but they are still stored by the server. It is up to clients to delete those filters eventually.

View File

@ -1,103 +0,0 @@
---
title: Libraries
description: List of libraries that work with the Mastodon API in various programming languages
menu:
docs:
parent: api
weight: -1
---
## Apex (Salesforce)
- [apex-mastodon](https://github.com/tzmfreedom/apex-mastodon)
## C# (.NET Standard)
- [Mastodot](https://github.com/yamachu/Mastodot)
- [Mastonet](https://github.com/glacasa/Mastonet)
- [TootNet](https://github.com/cucmberium/TootNet)
- [mastodon-api-cs](https://github.com/pawotter/mastodon-api-cs)
- [Mastodon.Net](https://github.com/Tlaster/Mastodon.Net)
## C++
- [mastodon-cpp](https://github.com/tastytea/mastodon-cpp)
## Crystal
- [mastodon.cr](https://github.com/decors/mastodon.cr)
## Common Lisp
- [tooter](https://github.com/Shinmera/tooter)
## Elixir
- [hunter](https://github.com/milmazz/hunter)
## Go
- [go-mastodon](https://github.com/mattn/go-mastodon)
- [madon](https://github.com/McKael/madon)
## Haskell
- [hastodon](https://github.com/syucream/hastodon)
## Java
- [mastodon4j](https://github.com/sys1yagi/mastodon4j)
## JavaScript
- [masto.js](https://github.com/neet/masto.js)
- [libodonjs](https://github.com/Zatnosk/libodonjs)
## JavaScript (Browser)
- [mastodon.js](https://github.com/Kirschn/mastodon.js)
## JavaScript (Node.js)
- [node-mastodon](https://github.com/jessicahayley/node-mastodon)
- [mastodon-api](https://github.com/vanita5/mastodon-api)
## Perl
- [Mastodon::Client](https://metacpan.org/pod/Mastodon::Client)
## PHP
- [Mastodon API for Laravel](https://github.com/kawax/laravel-mastodon-api)
- [Mastodon-api-php](https://github.com/yks118/Mastodon-api-php)
- [Composer based php API wrapper](https://github.com/r-daneelolivaw/mastodon-api-php)
- [MastodonOAuthPHP](https://github.com/TheCodingCompany/MastodonOAuthPHP)
- [Phediverse Mastodon REST Client](https://github.com/phediverse/mastodon-rest)
- [TootoPHP](https://framagit.org/MaxKoder/TootoPHP)
- [oauth2-mastodon](https://github.com/lrf141/oauth2-mastodon)
- [Mastodon Wordpress API](https://github.com/L1am0/mastodon_wordpress_api)
## Python
- [Mastodon.py](https://github.com/halcy/Mastodon.py)
## R
- [mastodon](https://github.com/ThomasChln/mastodon)
## Ruby
- [mastodon-api](https://github.com/tootsuite/mastodon-api)
## Rust
- [mammut](https://github.com/Aaronepower/mammut)
- [elefren](https://github.com/pwoolcoc/elefren)
## Scala
- [scaladon](https://github.com/schwitzerm/scaladon)
## Swift
- [MastodonKit](https://github.com/ornithocoder/MastodonKit)

View File

@ -0,0 +1,89 @@
---
title: OAuth Scopes
menu:
docs:
weight: 10
parent: api
---
## OAuth Scopes
The API is divided up into access scopes. The scopes are hierarchical, i.e. if you have access to `read`, you automatically have access to `read:accounts`. **It is recommended that you request as little as possible for your application.**
Multiple scopes can be requested at the same time: During app creation with the `scopes` param, and during the authorization phase with the `scope` query param \(space-separate the scopes\).
{{< hint style="info" >}}
Mind the `scope` vs `scopes` difference. This is because `scope` is a standard OAuth parameter name, so it is used in the OAuth methods. Mastodons 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.
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
- 0.9.0 - read, write, follow
- 2.4.0 - push
- 2.4.3 - granular scopes [https://github.com/tootsuite/mastodon/pull/7929](https://github.com/tootsuite/mastodon/pull/7929)
- 2.6.0 - read:reports deprecated \(unused stub\) [https://github.com/tootsuite/mastodon/pull/8736/commits/adcf23f1d00c8ff6877ca2ee2af258f326ae4e1f](https://github.com/tootsuite/mastodon/pull/8736/commits/adcf23f1d00c8ff6877ca2ee2af258f326ae4e1f)
- 2.6.0 - write:conversations added [https://github.com/tootsuite/mastodon/pull/9009](https://github.com/tootsuite/mastodon/pull/9009)
- 2.9.1 - Admin scopes added [https://github.com/tootsuite/mastodon/pull/9387](https://github.com/tootsuite/mastodon/pull/9387)
- 3.1.0 - Bookmark scopes added
## List of scopes
### `read`
Grants access to read data. Requesting `read` will also grant child scopes shown in the left column of the table below.
### `write`
Grants access to write data. Requesting `write` will also grant child scopes shown in the right column of the table below.
### `follow`
Grants access to manage relationships. Requesting `follow` will also grant the following child scopes, shown in bold in the table:
* `read:blocks`, `write:blocks`
* `read:follows`, `write:follows`
* `read:mutes`, `write:mutes`
### `push`
Grants access to [Web Push API subscriptions.]({{< relref "../methods/notifications/push.md" >}}) Added in Mastodon 2.4.0.
### Admin scopes
Used for moderation API. Added in Mastodon 2.9.1. The following granular scopes are available \(note that there is no singular `admin` scope\):
* `admin:read`
* `admin:read:accounts`
* `admin:read:reports`
* `admin:write`
* `admin:write:accounts`
* `admin:write:reports`
## Granular scopes
| read | write |
| :--- | :--- |
| read:accounts | write:accounts |
| **read:blocks** | **write:blocks** |
| read:bookmarks | write:bookmarks |
| | write:conversations |
| read:favourites | write:favourites |
| read:filters | write:filters |
| **read:follows** | **write:follows** |
| read:lists | write:lists |
| | write:media |
| **read:mutes** | **write:mutes** |
| read:notifications | write:notifications |
| | write:reports |
| read:search | |
| read:statuses | write:statuses |
| admin:read | admin:write |
| :--- | :--- |
| admin:read:accounts | admin:write:accounts |
| admin:read:reports | admin:write:reports |

View File

@ -1,44 +0,0 @@
---
title: Parameters
description: Specifics of parameter passing to the Mastodon API
menu:
docs:
parent: api
weight: 3
---
## Parameter format
Query strings, form data, and JSON submitted via POST body is equally understood by the API. It is expected that query strings are used for GET requests, and form data or JSON is used for all other requests.
## Arrays
An array parameter must encoded using bracket notation, e.g. `array[0]=foo&array[1]=bar` would be translated into:
```ruby
array = [
'foo',
'bar',
]
```
## Booleans
A boolean value is considered false for the values `0`, `f`, `F`, `false`, `FALSE`, `off`, `OFF`, considered to not be provided for empty strings, and considered to be true for all other values.
## Files
File uploads must be encoded using `multipart/form-data`.
## Nested parameters
Some parameters need to be nested. For that, bracket notation must also be used. For example, `source[privacy]=public&source[language]=en` would be translated into:
```ruby
source = {
privacy: 'public',
language: 'en',
}
```
This can be combined with arrays as well.

View File

@ -1,49 +0,0 @@
---
title: Permissions
description: Overview of OAuth 2 access scopes in Mastodon
menu:
docs:
parent: api
weight: 2
---
The API is divided up into access scopes:
|Scope|Parent(s)|Added in|
|:----|---------|:------:|
|`write`||0.9.0|
|`write:accounts`|`write`|2.4.3|
|`write:blocks`|`write`, `follow`|2.4.3|
|`write:favourites`|`write`|2.4.3|
|`write:filters`|`write`|2.4.3|
|`write:follows`|`write`, `follow`|2.4.3|
|`write:lists`|`write`|2.4.3|
|`write:media`|`write`|2.4.3|
|`write:mutes`|`write`, `follow`|2.4.3|
|`write:notifications`|`write`|2.4.3|
|`write:reports`|`write`|2.4.3|
|`write:statuses`|`write`|2.4.3|
|`read`||0.9.0|
|`read:accounts`|`read`|2.4.3|
|`read:blocks`|`read`, `follow`|2.4.3|
|`read:favourites`|`read`|2.4.3|
|`read:filters`|`read`|2.4.3|
|`read:follows`|`read`, `follow`|2.4.3|
|`read:lists`|`read`|2.4.3|
|`read:mutes`|`read`, `follow`|2.4.3|
|`read:notifications`|`read`|2.4.3|
|`read:reports`|`read`|2.4.3|
|`read:search`|`read`|2.4.3|
|`read:statuses`|`read`|2.4.3|
|`follow`||0.9.0|
|`push`||2.4.0|
The scopes are hierarchical, i.e. if you have access to `read`, you automatically have access to `read:accounts`. **It is recommended that you request as little as possible for your application.**
Multiple scopes can be requested at the same time: During app creation with the `scopes` param, and during the authorization phase with the `scope` query param (space-separate the scopes).
> **Note:** 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`.
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.

View File

@ -1,15 +0,0 @@
---
title: Web Push API
overview: How to use the Web Push API in Mastodon to receive push notifications in a native or browser app
menu:
docs:
parent: api
weight: 5
---
Mastodon natively supports the [Web Push API](https://developer.mozilla.org/en-US/docs/Web/API/Push_API). You can utilize the same mechanisms for your native app. It requires running a proxy server that connects to Android's and Apple's proprietary notification gateways. However, the proxy server does not have access to the contents of the notifications. For a reference, see [Mozilla's web push server](https://github.com/mozilla-services/autopush), or more practically, see:
- [toot-relay](https://github.com/DagAgren/toot-relay)
- [PushToFCM](https://github.com/tateisu/PushToFCM)
Using the Web Push API requires your app to have the `push` scope. To create a new Web Push API subscription, use [POST /api/v1/push/subscription]({{< relref "notifications.md#post-api-v1-push-subscription" >}}).

View File

@ -1,203 +0,0 @@
---
title: Accounts
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/accounts/:id
Returns [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:accounts" version="0.0.0" >}}
## POST /api/v1/accounts
Returns [Token]({{< relref "entities.md#token" >}})
The method is available to apps with a token obtained via the client credentials grant. It creates a user and account records, as well as an access token for the app that initiated the request. The user is unconfirmed, and an e-mail is sent as usual.
The method returns the access token, which the app should save for later. The REST API is not available to users with unconfirmed accounts, so the app must be smart to wait for the user to click a link in their e-mail inbox.
The method is rate-limited by IP to 5 requests per 30 minutes.
### Resource information
{{< api_method_info auth="Yes" user="No" scope="write write:accounts" version="2.7.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `username` | User name | Required |
| `email` | E-mail address | Required |
| `password` | Password text | Required |
| `agreement` | Agreement to local rules, terms of use, privacy policy (Bool) | Required |
| `locale` | The language of the e-mail to be sent first | Required |
The `agreement` parameter must be set to true after presenting the local rules, terms of use, privacy policy for the user and obtaining consent.
## GET /api/v1/accounts/verify_credentials
User's own account.
Returns [Account]({{< relref "entities.md#account" >}}) with an extra [`source` attribute]({{< relref "entities.md#source" >}}).
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:accounts" version="0.0.0" >}}
## PATCH /api/v1/accounts/update_credentials
Update user's own account.
Returns [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:accounts" version="0.0.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `display_name` | Display name | Optional |
| `note` | Biography | Optional |
| `avatar` | Avatar encoded using `multipart/form-data` | Optional |
| `header` | Header image encoded using `multipart/form-data` | Optional |
| `locked` | Enable follow requests | Optional |
| `source[privacy]` | Default post privacy preference | Optional |
| `source[sensitive]`| Whether to mark statuses as sensitive by default | Optional |
| `source[language]` | Override language on statuses by default (ISO6391) | Optional |
| `fields_attributes` | Profile metadata (max. 4) | Optional |
## GET /api/v1/accounts/:id/followers
Accounts which follow the given account.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="No" scope="read read:accounts" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## GET /api/v1/accounts/:id/following
Accounts which the given account is following.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="No" scope="read read:accounts" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## GET /api/v1/accounts/:id/statuses
An account's statuses.
Returns array of [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="No" scope="read read:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|Added in|
|----|-----------|:------:|:-----:|:------:|
| `only_media` | Only return statuses that have media attachments | Optional | false | |
| `pinned` | Only return statuses that have been pinned | Optional | false | |
| `exclude_replies` | Skip statuses that reply to other statuses | Optional | false | |
| `max_id` | Return results older than ID | Optional | | |
| `since_id` | Return results newer than ID | Optional | | |
| `min_id` | Return results immediately newer than ID | Optional | | |
| `limit` | Maximum number of results | Optional | 20 | | |
| `exclude_reblogs` | Skip statuses that are reblogs of other statuses | Optional | false | 2.7.0 |
### Pagination
{{< api_dynamic_pagination >}}
## POST /api/v1/accounts/:id/follow
Follow an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:follows follow" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `reblogs` | Whether the followed account's reblogs will show up in the home timeline | Optional | true |
## POST /api/v1/accounts/:id/unfollow
Unfollow an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:follows follow" version="0.0.0" >}}
## GET /api/v1/accounts/relationships
Relationship of the user to the given accounts in regards to following, blocking, muting, etc.
Returns array of [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:follows" version="0.0.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `id` | Array of account IDs | Required |
## GET /api/v1/accounts/search
Search for matching accounts by username, domain and display name.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:accounts" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `q` | What to search for | Required ||
| `limit` | Maximum number of results | Optional | 40 |
| `resolve` | Attempt WebFinger look-up | Optional | false |
| `following` | Only who the user is following | Optional | false |

View File

@ -1,38 +0,0 @@
---
title: Apps
menu:
docs:
parent: rest-api
weight: 10
---
## POST /api/v1/apps
Create a new application to obtain OAuth2 credentials.
Returns [App]({{< relref "entities.md#app" >}}) with `client_id` and `client_secret`
### Resource information
{{< api_method_info auth="No" user="No" version="0.0.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `client_name` | Name of your application | Required |
| `redirect_uris` | Where the user should be redirected after authorization | Required |
| `scopes` | Space separated list of [scopes]({{< relref "permissions.md" >}}) | Required |
| `website` | URL to the homepage of your app | Optional |
> To display the authorization code to the end-user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in `redirect_uris`
## GET /api/v1/apps/verify_credentials
Confirm that the app's OAuth2 credentials work.
Returns [App]({{< relref "entities.md#app" >}})
### Resource information
{{< api_method_info auth="Yes" user="No" version="2.0.0" >}}

View File

@ -1,47 +0,0 @@
---
title: Blocks
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/blocks
Accounts the user has blocked.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read:blocks follow" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## POST /api/v1/accounts/:id/block
Block an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:blocks follow" version="0.0.0" >}}
## POST /api/v1/accounts/:id/unblock
Unblock an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:blocks follow" version="0.0.0" >}}

View File

@ -1,17 +0,0 @@
---
title: Custom emoji
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/custom_emojis
Custom emojis that are available on the server.
Returns array of [Emoji]({{< relref "entities.md#emoji" >}})
### Resource information
{{< api_method_info auth="No" user="No" version="2.0.0" >}}

View File

@ -1,55 +0,0 @@
---
title: Domain blocks
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/domain_blocks
Domains the user has blocked.
Returns array of string.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:blocks follow" version="1.4.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## POST /api/v1/domain_blocks
Block a domain to hide all public posts from it, all notifications from it, and remove all followers from it.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:blocks follow" version="1.4.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `domain` | Domain to block| Required |
## DELETE /api/v1/domain_blocks
Remove a domain block.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:blocks follow" version="1.4.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `domain` | Domain to unblock| Required |

View File

@ -1,41 +0,0 @@
---
title: Endorsements
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/endorsements
Accounts the user chose to endorse.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:account" version="2.5.0" >}}
### Pagination
{{< api_pagination >}}
## POST /api/v1/accounts/:id/pin
Endorse an account, i.e. choose to feature the account on the user's public profile.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:accounts" version="2.5.0" >}}
## POST /api/v1/accounts/:id/unpin
Undo endorse of an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:accounts" version="2.5.0" >}}

View File

@ -1,43 +0,0 @@
---
title: Favourites
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/favourites
Statuses the user has favourited.
Returns array of [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:favourites" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 20 |
### Pagination
{{< api_pagination >}}
## POST /api/v1/statuses/:id/favourite
Favourite a status.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:favourites" version="0.0.0" >}}
## POST /api/v1/statuses/:id/unfavourite
Undo the favourite of a status.
Returns [Status]({{< relref "entities.md#status" >}})

View File

@ -1,75 +0,0 @@
---
title: Filters
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/filters
Text filters the user has configured that potentially must be applied client-side.
Returns array of [Filter]({{< relref "entities.md#filter" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:filters" version="2.4.3" >}}
## POST /api/v1/filters
Create a new filter.
Returns [Filter]({{< relref "entities.md#filter" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:filters" version="2.4.3" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `phrase` | Keyword or phrase to filter | Required |
| `context` | Array of strings that means filtering context. Each string is one of `home`, `notifications`, `public`, `thread`. At least one context must be specified. | Required |
| `irreversible` | Irreversible filtering will only work in `home` and `notifications` contexts by fully dropping the records. Otherwise, filtering is up to the client. | Optional |
| `whole_word` | Whether to consider word boundaries when matching | Optional |
| `expires_in` | Number that indicates seconds. Filter will be expire in seconds after API processed. Leave blank for no expiration | Optional |
## GET /api/v1/filters/:id
A text filter.
Returns [Filter]({{< relref "entities.md#filter" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:filters" version="2.4.3" >}}
## PUT /api/v1/filters/:id
Update a text filter.
Returns [Filter]({{< relref "entities.md#filter" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:filters" version="2.4.3" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `phrase` | Keyword or phrase to filter | Required |
| `context` | Array of strings that means filtering context. Each string is one of `home`, `notifications`, `public`, `thread`. At least one context must be specified. | Required |
| `irreversible` | Irreversible filtering will only work in `home` and `notifications` contexts by fully dropping the records. Otherwise, filtering is up to the client. | Optional |
| `whole_word` | Whether to consider word boundaries when matching | Optional |
| `expires_in` | Number that indicates seconds. Filter will be expire in seconds after API processed. Leave blank to not change | Optional |
## DELETE /api/v1/filters/:id
Delete a text filter.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:filters" version="2.4.3" >}}

View File

@ -1,43 +0,0 @@
---
title: Follow requests
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/follow_requests
Accounts that have requested to follow the user.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:follows follow" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## POST /api/v1/follow_requests/:id/authorize
Allow the account to follow the user.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:follows follow" version="0.0.0" >}}
## POST /api/v1/follow_requests/:id/reject
Do not allow the account to follow the user.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:follows follow" version="0.0.0" >}}

View File

@ -1,25 +0,0 @@
---
title: Follow suggestions
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/suggestions
Accounts the user had past positive interactions with, but is not following yet.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read" version="2.4.3" >}}
## DELETE /api/v1/suggestions/:account_id
Remove account from suggestions.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read" version="2.4.3" >}}

View File

@ -1,17 +0,0 @@
---
title: Instances
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/instance
Information about the server.
Returns [Instance]({{< relref "entities.md#instance" >}})
### Resource information
{{< api_method_info auth="No" user="No" version="0.0.0" >}}

View File

@ -1,127 +0,0 @@
---
title: Lists
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/lists
User's lists.
Returns array of [List]({{< relref "entities.md#list" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:lists" version="2.1.0" >}}
## GET /api/v1/accounts/:id/lists
User's lists that a given account is part of.
Returns array of [List]({{< relref "entities.md#list" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:lists" version="2.1.0" >}}
## GET /api/v1/lists/:id/accounts
Accounts that are in a given list.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:lists" version="2.1.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
>If you specify a `limit` of `0` in the query, all accounts will be returned without pagination. Otherwise, standard account pagination rules apply.
{{< api_pagination >}}
## GET /api/v1/lists/:id
Returns [List]({{< relref "entities.md#list" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:lists" version="2.1.0" >}}
## POST /api/v1/lists
Create a new list.
Returns [List]({{< relref "entities.md#list" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:lists" version="2.1.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `title` | The title of the list | Required |
## PUT /api/v1/lists/:id
Update a list.
Returns [List]({{< relref "entities.md#list" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:lists" version="2.1.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `title` | The title of the list | Required |
## DELETE /api/v1/lists/:id
Remove a list.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:lists" version="2.1.0" >}}
## POST /api/v1/lists/:id/accounts
Add accounts to a list.
> Only accounts already followed by the user can be added to a list.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:lists" version="2.1.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `account_ids` | Array of account IDs | Required |
## DELETE /api/v1/lists/:id/accounts
Remove accounts from a list.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:lists" version="2.1.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `account_ids` | Array of account IDs | Required |

View File

@ -1,46 +0,0 @@
---
title: Media attachments
menu:
docs:
parent: rest-api
weight: 10
---
## POST /api/v1/media
Upload a media attachment that can be used with a new status.
Returns [Attachment]({{< relref "entities.md#attachment" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:media" version="0.0.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `file` | Media file encoded using `multipart/form-data` | Required |
| `description` | A plain-text description of the media for accessibility (max 420 chars) | Optional |
| `focus` | Two floating points, comma-delimited. See [focal points](#focal-points) | Optional |
## PUT /api/v1/media/:id
Update a media attachment. Can only be done before the media is attached to a status.
Returns [Attachment]({{< relref "entities.md#attachment" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:media" version="0.0.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `description` | A plain-text description of the media for accessibility (max 420 chars) | Optional |
| `focus` | Two floating points, comma-delimited. See [focal points](#focal-points) | Optional |
## 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 for how to let users select focal point coordinates](https://github.com/jonom/jquery-focuspoint#1-calculate-your-images-focus-point).

View File

@ -1,73 +0,0 @@
---
title: Mutes
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/mutes
Accounts the user has muted.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read:mutes follow" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## POST /api/v1/accounts/:id/mute
Mute an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:mutes follow" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `notifications` | Whether the mute will mute notifications or not | Optional | true |
## POST /api/v1/accounts/:id/unmute
Unmute an account.
Returns [Relationship]({{< relref "entities.md#relationship" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write:mutes follow" version="0.0.0" >}}
## POST /api/v1/statuses/:id/mute
Mute the conversation the status is part of, to no longer be notified about it.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:mutes" version="1.4.2" >}}
## POST /api/v1/statuses/:id/unmute
Unmute the conversation the status is part of.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:mutes" version="1.4.2" >}}

View File

@ -1,117 +0,0 @@
---
title: Notifications
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/notifications
Notifications concerning the user.
Returns array of [Notification]({{< relref "entities.md#notification" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:notifications" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `max_id` | Return results older than ID | Optional ||
| `since_id` | Return results newer than ID | Optional ||
| `min_id` | Return results immediately newer than ID | Optional ||
| `limit` | Maximum number of results | Optional | 20 |
| `exclude_types` | Array of types to exclude (e.g. `follow`, `favourite`, `reblog`, `mention`) | Optional ||
| `account_id` | Return only notifications sent from given account | Optional ||
### Pagination
{{< api_dynamic_pagination >}}
## GET /api/v1/notifications/:id
Returns [Notification]({{< relref "entities.md#notification" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:notifications" version="0.0.0" >}}
## POST /api/v1/notifications/:id/dismiss
Delete a single notification from the server.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:notifications" version="0.0.0" >}}
## POST /api/v1/notifications/clear
Delete all notifications from the server.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:notifications" version="0.0.0" >}}
## POST /api/v1/push/subscription
Add a Web Push API subscription to receive notifications. See also: [Web Push API]({{< relref "push.md" >}})
> Each access token can have one push subscription. If you create a new subscription, the old subscription is deleted.
Returns [Push Subscription]({{< relref "entities.md#push-subscription" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="push" version="2.4.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `subscription[endpoint]` | Endpoint URL that called when notification is happen. | Required |
| `subscription[keys][p256dh]` | User agent public key. Base64 encoded string of public key of ECDH key using 'prime256v1' curve. | Required |
| `subscription[keys][auth]` | Auth secret. Base64 encoded string of 16 bytes of random data. | Required |
| `data[alerts][follow]` | Boolean of whether you want to receive follow notification event. | Optional |
| `data[alerts][favourite]` | Boolean of whether you want to receive favourite notification event. | Optional |
| `data[alerts][reblog]` | Boolean of whether you want to receive reblog notification event. | Optional |
| `data[alerts][mention]` | Boolean of whether you want to receive mention notification event. | Optional |
| `data[alerts][poll]` | Boolean of whether you want to receive poll result notification event. | Optional |
## GET /api/v1/push/subscription
Returns [Push Subscription]({{< relref "entities.md#push-subscription" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="push" version="2.4.0" >}}
## PUT /api/v1/push/subscription
Update current Web Push API subscription. Only the `data` part can be updated, e.g. which types of notifications are desired. To change fundamentals, a new subscription must be created instead.
Returns [Push Subscription]({{< relref "entities.md#push-subscription" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="push" version="2.4.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `data[alerts][follow]` | Boolean of whether you want to receive follow notification event. | Optional |
| `data[alerts][favourite]` | Boolean of whether you want to receive favourite notification event. | Optional |
| `data[alerts][reblog]` | Boolean of whether you want to receive reblog notification event. | Optional |
| `data[alerts][mention]` | Boolean of whether you want to receive mention notification event. | Optional |
| `data[alerts][poll]` | Boolean of whether you want to receive poll result notification event. | Optional |
## DELETE /api/v1/push/subscription
Remove the current Web Push API subscription.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="push" version="2.4.0" >}}

View File

@ -1,31 +0,0 @@
---
title: Polls
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/polls/:id
Returns [Poll]({{< relref "entities.md#poll" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="2.8.0" >}}
## POST /api/v1/polls/:id/votes
Vote on a poll.
Returns [Poll]({{< relref "entities.md#poll" >}})
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `choices` | Array of choice indices | Required |
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="2.8.0" >}}

View File

@ -1,24 +0,0 @@
---
title: Reports
menu:
docs:
parent: rest-api
weight: 10
---
## POST /api/v1/reports
Report an account.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:reports" version="1.1.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `account_id` | The ID of the account to report | Required |
| `status_ids` | The IDs of statuses to report as array | Optional |
| `comment` | Reason for the report (up to 1,000 characters) | Optional |
| `forward` | Whether to forward to the remote admin (in case of a remote account) | Optional |

View File

@ -1,51 +0,0 @@
---
title: Scheduled Statuses
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/scheduled_statuses
Get scheduled statuses.
Returns array of [ScheduledStatus]({{< relref "entities.md#scheduledstatus" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:statuses" version="2.7.0" >}}
## GET /api/v1/scheduled_statuses/:id
Get scheduled status.
Returns [ScheduledStatus]({{< relref "entities.md#scheduledstatus" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:statuses" version="2.7.0" >}}
## PUT /api/v1/scheduled_statuses/:id
Update Scheduled status. Only `scheduled_at` can be changed. To change the content, delete it and post a new status.
Returns [ScheduledStatus]({{< relref "entities.md#scheduledstatus" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="2.7.0" >}}
### Parameters
|Name|Description|Required|
|----|-----------|:------:|
| `scheduled_at` | Timestamp string to schedule posting of status (ISO 8601) | Optional |
## DELETE /api/v1/scheduled_statuses/:id
Remove Scheduled status.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="2.7.0" >}}

View File

@ -1,27 +0,0 @@
---
title: Search
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v2/search
Search for content in accounts, statuses and hashtags.
Returns [Results]({{< relref "entities.md#results" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:search" version="2.4.1" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `q` | The search query | Required ||
| `resolve` | Attempt WebFinger look-up | Optional |false|
| `limit` | Maximum number of results | Optional | 40 |
| `offset` | Offset in the search results | Optional | 0 |
| `following` | Only include accounts the user is following | Optional | false |

View File

@ -1,177 +0,0 @@
---
title: Statuses
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/statuses/:id
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
## GET /api/v1/statuses/:id/context
What the status replies to, and replies to it.
Returns [Context]({{< relref "entities.md#context" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
## GET /api/v1/statuses/:id/card
Link preview card for a status, if available.
Returns [Card]({{< relref "entities.md#card" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
## GET /api/v1/statuses/:id/reblogged_by
Accounts that reblogged the status.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## GET /api/v1/statuses/:id/favourited_by
Accounts that favourited the status.
Returns array of [Account]({{< relref "entities.md#account" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `limit` | Maximum number of results | Optional | 40 |
### Pagination
{{< api_pagination >}}
## POST /api/v1/statuses
Publish a new status.
Returns [Status]({{< relref "entities.md#status" >}})
When `scheduled_at` option is present,
Returns [ScheduledStatus]({{< relref "entities.md#scheduledstatus" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Added in|
|----|-----------|:------:|:------:|
| `status` | The text of the status | Optional\* |
| `in_reply_to_id` | ID of the status you want to reply to | Optional |
| `media_ids` | Array of media IDs to attach to the status | Optional\* |
| `poll` | Nested parameters to attach a poll to the status | Optional\* |2.8.0|
| `sensitive` | Mark the media in the status as sensitive | Optional |
| `spoiler_text` | Text to be shown as a warning before the actual content | Optional |
| `visibility` | One of `direct`, `private`, `unlisted` `public` | Optional |
| `scheduled_at` | Timestamp string to schedule posting of status (ISO 8601) | Optional |2.7.0|
| `language` | Override language code of the toot (ISO 639-2) | Optional |
> You must provide either `status` or `media_ids`, completely empty statuses are not allowed. Polls require a `status` and cannot be combined with `media_ids`.
Poll parameters:
|Name|Description|Required|
|----|-----------|:------:|
| `poll[options]` | Array of poll answer strings | Required |
| `poll[expires_in]` | Duration the poll should be open for in seconds | Required |
| `poll[multiple]` | Whether multiple choices should be allowed | Optional |
| `poll[hide_totals]` | Whether to hide totals until the poll ends | Optional |
### Idempotency
In order to prevent duplicate statuses, this endpoint accepts an `Idempotency-Key` header, which should be set to a unique string for each new status. In the event of a network error, a request can be retried with the same `Idempotency-Key`. Only one status will be created regardless of how many requests with the same `Idempotency-Key` did go through.
See <https://stripe.com/blog/idempotency> for more on idempotency and idempotency keys.
### Scheduled status
Allows users to schedule a toot (with media attachments) to be published at a certain future date.
The scheduled date must be at least 5 minutes into the future. At most, 300 toots can be scheduled at the same time. Only 50 toots can be scheduled for any given day.
When `scheduled_at` option is present, instead of creating a status, we only run status validation, and if it passes, we create an entry in scheduled_statuses which encodes the status attributes. Every 5 minutes, a scheduler iterates over the scheduled_statuses table to fetch the ones due in the next 5 minutes, and push them into a more precise Sidekiq queue. In Sidekiq, the individual statuses are created, with media attachments being unassigned from the scheduled status and assigned to the real one.
This option was added since v2.7.0.
## DELETE /api/v1/statuses/:id
Remove a status. The status may still be available a short while after the call.
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="0.0.0" >}}
## POST /api/v1/statuses/:id/reblog
Reblog a status.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="0.0.0" >}}
## POST /api/v1/statuses/:id/unreblog
Undo the reblog of a status.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:statuses" version="0.0.0" >}}
## POST /api/v1/statuses/:id/pin
Pin user's own status to user's profile.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:accounts" version="1.6.0" >}}
## POST /api/v1/statuses/:id/unpin
Remove pinned status from user's profile.
Returns [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="write write:accounts" version="1.6.0" >}}

View File

@ -1,123 +0,0 @@
---
title: Timelines
menu:
docs:
parent: rest-api
weight: 10
---
## GET /api/v1/timelines/home
Statuses from accounts the user follows.
Returns array of [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `max_id` | Return results older than ID | Optional ||
| `since_id` | Return results newer than ID | Optional ||
| `min_id` | Return results immediately newer than ID | Optional ||
| `limit` | Maximum number of results | Optional | 20 |
## GET /api/v1/conversations
Conversations for an account
Returns array of [Conversation]({{< relref "entities.md#conversation" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:statuses" version="2.6.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `max_id` | Return results older than ID | Optional ||
| `since_id` | Return results newer than ID | Optional ||
| `min_id` | Return results immediately newer than ID | Optional ||
| `limit` | Maximum number of results | Optional | 20 |
### Pagination
{{< api_dynamic_pagination >}}
## GET /api/v1/timelines/public
Public statuses known to the server.
Returns array of [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `local` | Only local statuses | Optional |false|
| `only_media` | Only statuses with media attachments | Optional |false|
| `max_id` | Return results older than ID | Optional ||
| `since_id` | Return results newer than ID | Optional ||
| `min_id` | Return results immediately newer than ID | Optional ||
| `limit` | Maximum number of results | Optional | 20 |
### Pagination
{{< api_dynamic_pagination >}}
## GET /api/v1/timelines/tag/:hashtag
Public statuses known to the server marked with a given hashtag.
Returns array of [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="No" user="No" scope="read read:statuses" version="0.0.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `local` | Only local statuses | Optional |false|
| `only_media` | Only statuses with media attachments | Optional |false|
| `max_id` | Return results older than ID | Optional ||
| `since_id` | Return results newer than ID | Optional ||
| `min_id` | Return results immediately newer than ID | Optional ||
| `limit` | Maximum number of results | Optional | 20 |
### Pagination
{{< api_dynamic_pagination >}}
## GET /api/v1/timelines/list/:list_id
Statuses from accounts on a given list.
Returns array of [Status]({{< relref "entities.md#status" >}})
### Resource information
{{< api_method_info auth="Yes" user="Yes" scope="read read:statuses" version="2.1.0" >}}
### Parameters
|Name|Description|Required|Default|
|----|-----------|:------:|:-----:|
| `max_id` | Return results older than ID | Optional ||
| `since_id` | Return results newer than ID | Optional ||
| `min_id` | Return results immediately newer than ID | Optional ||
| `limit` | Maximum number of results | Optional | 20 |
### Pagination
{{< api_dynamic_pagination >}}

View File

@ -1,82 +0,0 @@
---
title: Streaming API
description: How to use Mastodon's streaming API for live, real-time updates
menu:
docs:
parent: api
weight: 4
---
Your application can use a [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) endpoint to receive updates in real-time. Server-sent events is an incredibly simple transport method that relies entirely on chunked-encoding transfer, i.e. the HTTP connection is kept open and receives new data periodically.
Alternatively, a WebSocket connection can also be established.
## Server-sent events (HTTP)
### Endpoints
#### GET /api/v1/streaming/health
Returns `OK` when streaming service is fine
#### GET /api/v1/streaming/user
Returns events that are relevant to the authorized user, i.e. home timeline and notifications
#### GET /api/v1/streaming/public
Returns all public statuses
#### GET /api/v1/streaming/public/local
Returns all local statuses
#### GET /api/v1/streaming/hashtag?tag=:hashtag
Returns all public statuses for a particular hashtag
#### GET /api/v1/streaming/hashtag/local?tag=:hashtag
Returns all local statuses for a particular hashtag
#### GET /api/v1/streaming/list?list=:list_id
Returns statuses for a list
#### GET /api/v1/streaming/direct
Returns all direct messages
### Stream contents
The stream will contain events as well as heartbeat comments. Lines that begin with a colon (`:`) can be ignored by parsers, they are simply there to keep the connection open. Events have this structure:
```
event: name
data: payload
```
## WebSocket
For WebSockets, there is only one URL path (`/api/v1/streaming`). The access token as well as the endpoint you are interested in must be provided with query params, respectively `access_token` and `stream`. Query params `list` and `tag` are likewise supported for relevant endpoints.
Possible `stream` values:
- `user`
- `public`
- `public:local`
- `hashtag`
- `hashtag:local`
- `list`
- `direct`
## Event types
|Event|Description|What's in the payload|
|-----|-----------|---------------------|
|`update`|A new status has appeared|[Status]({{< relref "entities.md#status" >}})|
|`notification`|A new notification has appeared|[Notification]({{< relref "entities.md#notification" >}})|
|`delete`|A status has been deleted|ID of the deleted status|
|`filters_changed`|Keyword filters have been changed||
The payload is JSON-encoded.
> **Note:** In case of `filters_changed` event, `payload` is not defined.

View File

@ -0,0 +1,124 @@
---
title: Logging in with an account
description: How to obtain authorization from a user and perform actions on their behalf.
menu:
docs:
weight: 40
parent: user
---
## Scopes explained
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.
Scopes must be a subset. When we created our app, we specified `read write follow push` -- we could simply request all available scopes by specifying `read write follow push`, but it is a better idea to only request what your app will actually need through granular scopes. See [OAuth Scopes](../api/oauth-scopes.md) for a full list of scopes. Each API method's documentation will also specify the OAuth access level and scope required to call it.
## **Example authorization code flow**
This is similar to the authentication flow from before, but this time, we need to obtain authorization from a user as well.
### Client ID and secret
First, if you have not already registered a client application, then see [Creating our application](token.md#creating-our-application) on the previous page or go directly to [POST /api/v1/apps](../methods/apps/#create-an-application) for the full documentation of that method. We will need the `client_id` and `client_secret` for our application.
### Authorize the user
To authorize a user, request [GET /oauth/authorize](../methods/apps/oauth.md#authorize-a-user) in a browser with the following query parameters:
```bash
https://mastodon.example/oauth/authorize
?client_id=CLIENT_ID
&scope=read+write+follow+push
&redirect_uri=urn:ietf:wg:oauth:2.0:oob
&response_type=code
```
Note the following:
* `client_id` and `client_secret` were obtained when registering our application.
* `scope` must be a subset of our registered app's registered scopes. It is a good idea to only request what you need. See [OAuth Scopes](../api/oauth-scopes.md) for more information.
* `redirect_uri` is one of the URIs we registered with our app. We are still using "out of band" for this example, which means we will have to manually copy and paste the resulting code, but if you registered your application with a URI that you control, then the code will be returned as a query parameter `code` and can be logged by your request handler. See the response section of the API method documentation for more information on this.
### Obtain the token
Now that we have an authorization `code`, let's obtain an access token that will authenticate our requests as the authorized user. To do so, use [POST /oauth/token](../methods/apps/oauth.md#obtain-a-token) like before, but pass the authorization code we just obtained:
```bash
curl -X POST \
-F 'client_id=your_client_id_here' \
-F 'client_secret=your_client_secret_here' \
-F 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \
-F 'grant_type=authorization_code' \
-F 'code=user_authzcode_here' \
-F 'scope=read write follow push' \
https://mastodon.example/oauth/token
```
Note the following:
* `client_id` and `client_secret` were provided in the response text when you registered your application.
* `redirect_uri` must be one of the URIs defined when registering the application.
* We are requesting a `grant_type` of `authorization_code`, which still defaults to giving us the `read` scope. However, while authorizing our user, we requested a certain `scope` -- pass the exact same value here.
* The `code` can only be used once. If you need to obtain a new token, you will need to have the user authorize again by repeating the above [Authorize the user](authorized.md#authorize-the-user) step.
The response of this method is a [Token]({{< relref "../entities/token.md" >}}) entity. We will need the `access_token` value. Once you have the access token, save it in your local cache. To use it in requests, add the HTTP header `Authorization: Bearer ...` to any API call that requires OAuth \(i.e., one that is not publicly accessible\). Let's verify that our obtained credentials are working by calling [GET /api/v1/accounts/verify\_credentials](../methods/accounts/#verify-account-credentials):
```bash
curl \
-H 'Authorization: Bearer our_access_token_here' \
https://mastodon.example/api/v1/accounts/verify_credentials
```
If we've obtained our token and formatted our request correctly, we should see our details returned to us as an [Account]({{< relref "../entities/account.md" >}}) entity, with the `source` parameter included.
## Performing actions as the authorized user
With our OAuth token for the authorized user, we can now perform any action as that user that is within our token's scope.
### Publish and delete statuses
* See [POST /api/v1/statuses](../methods/statuses/#publish-new-status) for how to create statuses.
* See [/api/v1/media]({{< relref "../methods/statuses/media.md" >}}) for creating media attachments.
* See [/api/v1/scheduled\_statuses]({{< relref "../methods/statuses/scheduled_statuses.md" >}}) for managing scheduled statuses.
### Interact with timelines
* See [/api/v1/timelines]({{< relref "../methods/timelines/" >}}) for accessing timelines.
* See [/api/v1/markers]({{< relref "../methods/timelines/markers.md" >}}) for saving and loading positions in timelines.
* See [/api/v1/statuses]({{< relref "../methods/statuses/" >}}) for performing actions on statuses.
* See [/api/v1/polls]({{< relref "../methods/statuses/polls.md" >}}) for viewing and voting on polls.
* See [/api/v1/lists]({{< relref "../methods/timelines/lists.md" >}}) for obtaining list IDs to use with [GET /api/v1/timelines/list/:list\_id](../methods/timelines/#list-timeline).
* See [/api/v1/conversations]({{< relref "../methods/timelines/conversations.md" >}}) for obtaining direct conversations.
* See [/api/v1/favourites]({{< relref "../methods/accounts/favourites.md" >}}) for listing favourites.
* See [/api/v1/bookmarks]({{< relref "../methods/accounts/bookmarks.md" >}}) for listing bookmarks.
### Interact with other users
* See [/api/v1/accounts]({{< relref "../methods/accounts/" >}}) for performing actions on other users.
* See [/api/v1/follow\_requests]({{< relref "../methods/accounts/follow_requests.md" >}}) for handling follow requests.
* See [/api/v1/mutes]({{< relref "../methods/accounts/mutes.md" >}}) for listing mutes.
* See [/api/v1/blocks]({{< relref "../methods/accounts/blocks.md" >}}) for listing blocks.
### Receive notifications
* See [/api/v1/notifications]({{< relref "../methods/notifications/" >}}) for managing a user's notifications.
* See [/api/v1/push]({{< relref "../methods/notifications/push.md" >}}) for subscribing to push notifications.
### Discovery features
* See [/api/v2/search]({{< relref "../methods/search.md" >}}) for querying resources.
* See [/api/v1/suggestions]({{< relref "../methods/accounts/suggestions.md" >}}) for suggested accounts to follow.
### Use safety features
* See [/api/v1/filters]({{< relref "../methods/accounts/filters.md" >}}) for managing filtered keywords.
* See [/api/v1/domain\_blocks]({{< relref "../methods/accounts/domain_blocks.md" >}}) for managing blocked domains.
* See [/api/v1/reports]({{< relref "../methods/accounts/reports.md" >}}) for creating reports.
* See [/api/v1/admin]({{< relref "../methods/admin.md" >}}) for moderator actions.
### Manage account info
* See [/api/v1/endorsements]({{< relref "../methods/accounts/endorsements.md" >}}) for managing a user profile's featured accounts.
* See [/api/v1/featured\_tags]({{< relref "../methods/accounts/featured_tags.md" >}}) for managing a user profile's featured hashtags.
* See [/api/v1/preferences]({{< relref "../methods/accounts/preferences.md" >}}) for reading user preferences.

View File

@ -0,0 +1,53 @@
---
title: Guidelines and best practices
menu:
docs:
weight: 50
parent: client
---
## Login <a id="login"></a>
**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.
## Usernames <a id="usernames"></a>
**Decentralization must be transparent to the user**. It should be possible to see that a given user is from another server, by e.g. 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
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.**
With that said, because IDs are string representations of numbers, they can still be sorted chronologically very easily by doing the following:
1. Sort by size. Newer statuses will have longer IDs.
2. Sort lexically. Newer statuses will have at least one digit that is higher when compared positionally.
## Formatting <a id="formatting"></a>
Plain text is not available for content from remote servers, and plain text syntax rules may vary wildly between Mastodon and other fediverse applications. For certain attributes, such as the content of statuses, **Mastodon provides sanitized HTML**. You may expect these tags to appear in the content: `<p>`, `<br>`, `<span>`, `<a>`. See [HTML Sanitization](../spec/activitypub.md#html-sanitization) for more details.
### Mentions, hashtags, and custom emoji <a id="mentions-and-hashtags"></a>
Mentions and hashtags are `<a>` 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.md" >}}), which can be matched to a particular tag. See [Status &gt; Rendering attributes](../entities/status.md#rendering-attributes) for more information.
### Link shortening <a id="other-links"></a>
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:
```markup
<a href="https://example.com/page/that/is/very/long">
<span class="invisible">https://</span>
<span class="ellipsis">example.com/page</span>
<span class="invisible">/that/is/very/long</span>
</a>
```
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 <a id="filters"></a>
Clients must do their own text filtering based on filters returned from the API. The server will apply `irreversible` filters for home and notifications context, but anything else is still up to the client to filter!
Expired filters are not deleted by the server. They should no longer be applied but they are still stored by the server, as users may update the expiry time to re-enable the filter. It is up to users to delete those filters eventually.

136
content/en/client/intro.md Normal file
View File

@ -0,0 +1,136 @@
---
title: Getting started with the API
description: 'A primer on REST APIs, HTTP requests and responses, and parameters.'
menu:
docs:
weight: 10
parent: client
---
## An introduction to 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.
## Understanding HTTP requests and responses
REST API endpoints can be called with certain HTTP methods, and more than one method can be used on the same endpoint. The Mastodon API will generally use the following HTTP methods:
* **GET**: Read or view a resource.
* **POST**: Send information to the server.
* **PUT** \| **PATCH**: Update a resource.
* **DELETE**: Removes a resource.
Your favorite programming language probably has a utility or library to make HTTP requests. For the purposes of this section, the cURL utility will be used for examples, which is a command-line utility included with many operating systems by default \(as `curl`\).
With cURL, the default HTTP method is GET, but you can specify the type of request to make by using the `--request` or `-X` flag; for example, `curl -X POST` will send a POST request instead of a GET request. You may also want to use the `-i` flag to include additional HTTP headers that may be returned as part of the response where relevant.
## Providing parameters
HTTP requests can include additional parameters in various different ways, but most notably, the Mastodon API understands query strings, form data, and JSON.
{{< hint style="info" >}}
Query strings, form data, and JSON submitted via POST body are equally understood by the API. It is expected that query strings are used for GET requests, and form data or JSON is used for all other requests.
{{< /hint >}}
### Query strings
Simply request the URL, but append query strings to the end. Query strings can be appended by first typing ? and then appending them in the form of parameter=value. Multiple query strings can be appended by separating them with &. For example:
```bash
curl https://mastodon.example/endpoint?q=test&n=0
```
### Form data
Instead of mutating the URL with query strings, you can send the data separately. With cURL, this is done by passing it with the `--data` or `-d` flag. Data may be sent together similar to query strings, or it may be sent separately as key-value pairs with multiple data flags. You may also use the `--form` or `-F` flag for key-value pairs, which also allows sending multipart data such as files. For example:
```bash
# send raw data as query strings
curl -X POST \
-d 'q=test&n=0' \
https://mastodon.example/endpoint
# send raw data separately
curl -X POST \
-d 'q=test' \
-d 'n=0' \
https://mastodon.example/endpoint
# explicit form-encoded; allows for multipart data
curl -X POST \
-F 'q=test' \
-F 'n=0' \
-F 'file=@filename.jpg' \
https://mastodon.example/endpoint
```
### JSON
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:
```bash
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"parameter": "value"}' \
https://mastodon.example/endpoint
```
## Data types
### Multiple values \(Array\)
An array parameter must encoded using bracket notation, e.g. `array[]=foo&array[]=bar` would be translated into the following:
```ruby
array = [
'foo',
'bar',
]
```
As JSON, arrays are formatted like so:
```javascript
{
"array": ["foo", "bar"]
}
```
### Nested parameters \(Hash\)
Some parameters need to be nested. For that, bracket notation must also be used. For example, `source[privacy]=public&source[language]=en` would be translated into:
```ruby
source = {
privacy: 'public',
language: 'en',
}
```
As JSON, hashes are formatted like so:
```javascript
{
"source": {
"privacy": "public",
"language", "en"
}
}
```
### True-or-false \(Booleans\)
A boolean value is considered false for the values `0`, `f`, `F`, `false`, `FALSE`, `off`, `OFF`, considered to not be provided for empty strings, and considered to be true for all other values. When using JSON data, use the literals `true`, `false`, and `null` instead.
### Files
File uploads must be encoded using `multipart/form-data`.
This can be combined with arrays as well.
## How to use API response data
The Mastodon REST API will return JSON as the response text. It also returns HTTP headers which may be useful in handling the response, as well as an HTTP status code which should let you know how the server handled the request. The following HTTP status codes may be expected:
* 200 = OK. The request was handled successfully.
* 4xx = Client error. Your request was not correct. Most commonly, you may see 401 Unauthorized, 404 Not Found, 410 Gone, or 422 Unprocessed.
* 5xx = Server error. Something went wrong while handling the request. Most commonly, you may see 503 Unavailable.

View File

@ -0,0 +1,104 @@
---
title: Libraries and implementations
description: Interface with the Mastodon API in the programming language of your choice.
menu:
docs:
weight: 60
parent: client
---
## Apex \(Salesforce\) <a id="apex-salesforce"></a>
* [apex-mastodon](https://github.com/tzmfreedom/apex-mastodon)
## C\# \(.NET Standard\) <a id="c-net-standard"></a>
* [Mastodot](https://github.com/yamachu/Mastodot)
* [Mastonet](https://github.com/glacasa/Mastonet)
* [TootNet](https://github.com/cucmberium/TootNet)
* [mastodon-api-cs](https://github.com/pawotter/mastodon-api-cs)
* [Mastodon.Net](https://github.com/Tlaster/Mastodon.Net)
## C++ <a id="c"></a>
* [mastodon-cpp](https://github.com/tastytea/mastodon-cpp)
## Crystal <a id="crystal"></a>
* [mastodon.cr](https://github.com/decors/mastodon.cr)
## Common Lisp <a id="common-lisp"></a>
* [tooter](https://github.com/Shinmera/tooter)
## Elixir <a id="elixir"></a>
* [hunter](https://github.com/milmazz/hunter)
## Go <a id="go"></a>
* [go-mastodon](https://github.com/mattn/go-mastodon)
* [madon](https://github.com/McKael/madon)
## Haskell <a id="haskell"></a>
* [hastodon](https://github.com/syucream/hastodon)
## Java <a id="java"></a>
* [mastodon4j](https://github.com/sys1yagi/mastodon4j)
## JavaScript <a id="javascript"></a>
* [masto.js](https://github.com/neet/masto.js)
* [libodonjs](https://github.com/Zatnosk/libodonjs)
## JavaScript \(Browser\) <a id="javascript-browser"></a>
* [mastodon.js](https://github.com/Kirschn/mastodon.js)
## JavaScript \(Node.js\) <a id="javascript-node-js"></a>
* [node-mastodon](https://github.com/jessicahayley/node-mastodon)
* [mastodon-api](https://github.com/vanita5/mastodon-api)
## Perl <a id="perl"></a>
* [Mastodon::Client](https://metacpan.org/pod/Mastodon::Client)
## PHP <a id="php"></a>
* [Mastodon API for Laravel](https://github.com/kawax/laravel-mastodon-api)
* [Mastodon-api-php](https://github.com/yks118/Mastodon-api-php)
* [Composer based php API wrapper](https://github.com/r-daneelolivaw/mastodon-api-php)
* [MastodonOAuthPHP](https://github.com/TheCodingCompany/MastodonOAuthPHP)
* [Phediverse Mastodon REST Client](https://github.com/phediverse/mastodon-rest)
* [TootoPHP](https://framagit.org/MaxKoder/TootoPHP)
* [oauth2-mastodon](https://github.com/lrf141/oauth2-mastodon)
* [Mastodon Wordpress API](https://github.com/L1am0/mastodon_wordpress_api)
## Python <a id="python"></a>
* [Mastodon.py](https://github.com/halcy/Mastodon.py)
## R <a id="r"></a>
* [mastodon](https://github.com/ThomasChln/mastodon)
## Ruby <a id="ruby"></a>
* [mastodon-api](https://github.com/tootsuite/mastodon-api)
## Rust <a id="rust"></a>
* [mammut](https://github.com/Aaronepower/mammut)
* [elefren](https://github.com/pwoolcoc/elefren)
## Scala <a id="scala"></a>
* [scaladon](https://github.com/schwitzerm/scaladon)
## Swift <a id="swift"></a>
* [MastodonKit](https://github.com/ornithocoder/MastodonKit)

109
content/en/client/public.md Normal file
View File

@ -0,0 +1,109 @@
---
title: Playing with public data
description: Familiarizing yourself with endpoints and entities.
menu:
docs:
weight: 20
parent: client
---
Now that you know how to construct HTTP requests using cURL or your favorite programming language's HTTP utility or library, it is time to learn about endpoints and responses.
## Endpoints explained
All HTTP requests are made against a target URL. When you request data to or from a website, you do so by using a specific URL. Depending on the URL, your request will be interpreted by the HTTP server and the appropriate response will be returned to you.
Examples will be written using the fictional Mastodon website, mastodon.example, which is hosted at `https://mastodon.example`. The root of this website is `/`, and specific subdirectories and paths are known as endpoints. Mastodon's API endpoints are nested under the `/api` namespace, and most methods currently have their endpoints under `/api/v1`. Requests will be listed by their HTTP method and their endpoint; for example, GET /api/v1/endpoint should be interpreted as a GET request made to that endpoint on your domain, or in other words, `https://mastodon.example/api/v1/endpoint`.
## Fetching public timelines
Let's take a look at one of the most basic use cases for public data from Mastodon -- the public timelines.
We can try to request [GET /api/v1/timelines/public](../methods/timelines/#public-timeline) like so:
```bash
curl https://mastodon.example/api/v1/timelines/public
```
Wow, that's a lot of text in response! The public timeline returns 20 statuses by default. We can use the `limit` parameter to request less than that. Let's try requesting the same endpoint, but with a limit of 2 this time:
```bash
curl https://mastodon.example/api/v1/timelines/public?limit=2
```
Our response should be more manageable this time. We can parse or beautify this JSON with our chosen utility, and we should see that the response looks something like this:
```javascript
[
{
"id": "103206804533200177",
"created_at": "2019-11-26T23:27:31.000Z",
...
"visibility": "public",
...
},
{
"id": "103206804086086361",
"created_at": "2019-11-26T23:27:32.000Z",
...
"visibility": "public",
...
}
]
```
We can do similarly for hashtags by calling [GET /api/v1/timelines/tag/:hashtag](../methods/timelines/#hashtag-timeline) -- here, the colon means that this part of the endpoint is a path parameter. In the case of :hashtag, this means we use the hashtag's name \(and once again, a limit of 2\):
```bash
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.md" >}}) 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
import json
response = requests.get("https://mastodon.example/api/v1/timelines/tag/cats?limit=2")
statuses = json.dumps(response.text) # this converts the json to a python list of dictionary
assert statuses[0]["visibility"] == "public" # we are reading a public timeline
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.
{{< /hint >}}
{{< hint style="info" >}}
[MastoVue](https://mastovue.glitch.me) is an example of an application that lets you browse public timelines.
{{< /hint >}}
## Fetching public accounts and statuses
Now that we are familiar with how to make requests and how to handle responses, you can experiment with more public data. The following methods may be of interest:
* Once you know an account's id, you can use [GET /api/v1/accounts/:id](../methods/accounts/#account) to view the [Account]({{< relref "../entities/account.md" >}}) entity.
* To view public statuses posted by that account, you can use [GET /api/v1/accounts/:id/statuses](../methods/accounts/#statuses) and parse the resulting array of [Status]({{< relref "../entities/status.md" >}}) entities.
* Once you know a status's id, you can use [GET /api/v1/statuses/:id](../methods/statuses/#view-specific-status) to view the Status entity.
* You can also use [GET /api/v1/statuses/:id/reblogged\_by](../methods/statuses/#boosted-by) to view who boosted that status,
* or [GET /api/v1/statuses/:id/favourited\_by](../methods/statuses/#favourited-by) to view who favourited that status.
* Requesting [GET /api/v1/statuses/:id/context](../methods/statuses/#parent-and-child-statuses) will show you the ancestors and descendants of that status in the tree that is the conversational thread.
* If the status has a poll attached, you can use [GET /api/v1/polls/:id](../methods/statuses/polls.md#view-a-poll) to view the poll separately.
IDs of accounts and statuses are local to the Mastodon website's database and will differ for each Mastodon website.
## Fetching public instance data
One last thing you can do with anonymous requests is to view information about the Mastodon website.
* View general information with [GET /api/v1/instance](../methods/instance/#fetch-instance),
* view its peers with [GET /api/v1/instance/peers](../methods/instance/#list-of-connected-domains) or
* its weekly activity with [GET /api/v1/instance/activity](../methods/instance/#weekly-activity), or to
* list all custom emoji available with [GET /api/v1/custom\_emojis](../methods/instance/custom_emojis.md#custom-emoji).
* See [GET /api/v1/directory](../methods/instance/directory.md#view-profile-directory) for a directory of all available profiles.
* See [GET /api/v1/trends](../methods/instance/trends.md#trending-tags) for currently trending hashtags.
{{< hint style="info" >}}
For a practical example of what you can do with just instance data, see [emojos.in](https://emojos.in/), which lets you preview all custom emoji at a given instance.
{{< /hint >}}

View File

@ -0,0 +1,68 @@
---
title: Obtaining client app access
description: Getting accustomed to the basics of authentication and authorization.
menu:
docs:
weight: 30
parent: client
---
## Authentication and authorization
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.md" >}}) 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.
## **Creating our application**
The first thing we will need to do is to register an application, in order to be able to generate access tokens later. The application can be created like so:
```bash
curl -X POST \
-F 'client_name=Test Application' \
-F 'redirect_uris=urn:ietf:wg:oauth:2.0:oob' \
-F 'scopes=read write follow push' \
-F 'website=https://myapp.example' \
https://mastodon.example/api/v1/apps
```
In the above example, we specify the client name and website, which will be shown on statuses if applicable. But more importantly, note the following two parameters:
* `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](../api/oauth-scopes.md) 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](../methods/apps/#create-an-application) for more details on registering applications.
## **Example authentication code flow**
Now that we have an application, let's obtain an access token that will authenticate our requests as that client application. To do so, use [POST /oauth/token](../methods/apps/oauth.md#obtain-a-token) like so:
```bash
curl -X POST \
-F 'client_id=your_client_id_here' \
-F 'client_secret=your_client_secret_here' \
-F 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \
-F 'grant_type=client_credentials' \
https://mastodon.example/oauth/token
```
Note the following:
* `client_id` and `client_secret` were provided in the response text when you registered your application.
* `redirect_uri` must be one of the URIs defined when registering the application.
* We are requesting a `grant_type` of `client_credentials`, which defaults to giving us the `read` scope.
The response of this method is a [Token]({{< relref "../entities/token.md" >}}) entity. We will need the `access_token` value. Once you have the access token, save it in your local cache. To use it in requests, add the HTTP header `Authorization: Bearer ...` to any API call that requires OAuth \(i.e., one that is not publicly accessible\). Let's verify that our obtained credentials are working by calling [GET /api/v1/apps/verify\_credentials](../methods/apps/#verify-your-app-works):
```bash
curl \
-H 'Authorization: Bearer our_access_token_here' \
https://mastodon.example/api/v1/apps/verify_credentials
```
If we've obtained our token and formatted our request correctly, we should see our details returned to us as an [Application]({{< relref "../entities/application.md" >}}) entity.
## What we can do with authentication
With our authenticated client application, we can view relations of an account with [GET /api/v1/accounts/:id/following](../methods/accounts/#following) and [GET /api/v1/accounts/:id/followers](../methods/accounts/#followers). Also, some instances may require authentication for methods that would otherwise be public, so if you encountered any authentication errors while playing around with public methods, then those methods should now work.

60
content/en/dev/code.md Normal file
View File

@ -0,0 +1,60 @@
---
title: Code structure
menu:
docs:
weight: 30
parent: dev
---
{{< hint style="danger" >}}
This page is under construction.
{{< /hint >}}
### Code structure <a id="code-structure"></a>
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 <a id="ruby-1"></a>
| Path | Description |
| :--- | :--- |
| `app/controllers` | Code that binds business logic to templates |
| `app/helpers` | Code that can be used from views, i.e. common operations |
| `app/lib` | Code that doesnt fit in the other categories |
| `app/models` | Code that represents data entities |
| `app/serializers` | Code that generates JSON from models |
| `app/services` | Complex logical operations involving multiple models |
| `app/views` | Templates for generating HTML or other output |
| `app/workers` | Code that executes outside the request-response cycle |
| `spec` | Automated test suite |
#### JavaScript <a id="javascript-1"></a>
| Path | Description |
| :--- | :--- |
| `app/javascript/mastodon` | Code for the multi-column React.js application |
| `app/javascript/packs` | Code for non-React.js pages |
#### CSS and other assets <a id="css-and-other-assets"></a>
| Path | Description |
| :--- | :--- |
| `app/javascript/images` | Images |
| `app/javascript/styles` | Code that turns into CSS via Sass |
#### Localizations <a id="localizations"></a>
| Path | Description |
| :--- | :--- |
| `config/locales` | Server-side localizations in the YML format |
| `app/javascript/mastodon/locales` | Client-side localizations in the JSON format |
### Localization maintenance <a id="localization-maintenance"></a>
All locale files are normalized to ensure consistent formatting and key order, which minimizes changesets in version control.
| Command | Description |
| :--- | :--- |
| `i18n-tasks normalize` | Normalize server-side translations |
| `yarn run manage:translations` | Normalize client-side translations |

View File

@ -0,0 +1,30 @@
---
title: Technical overview
menu:
docs:
weight: 10
parent: dev
---
{{< hint style="warning" >}}
This page is under construction.
{{< /hint >}}
Mastodon is a Ruby on Rails application with a React.js front-end. It follows standard practices of those frameworks, so if you are already familiar with Rails or React.js, you will not find any surprises here.
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.
### Environments <a id="environments"></a>
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.
The default value of `RAILS_ENV` is `development`, so you dont need to set anything extra to run Mastodon in development mode. In fact, all of Mastodons 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:
* Ruby code reloads itself when you change it, which means you dont need to restart the Rails server process to see changes
* All errors you encounter show stack traces in the browser, rather than being hidden behind a generic error page
* Webpack runs continuously and re-compiles JS and CSS assets when you change any of the front-end files, and the pages automatically reload
* Caching is disabled by default
* An admin account with the e-mail `admin@localhost:3000` and password `mastodonadmin` is created automatically during `db:seed`
It should be noted that the Docker configuration distributed with Mastodon is optimized for the production environment, and so is an extremely bad fit for development. The Vagrant configuration, on the other hand, is meant specifically for development and not production use.

209
content/en/dev/routes.md Normal file
View File

@ -0,0 +1,209 @@
---
title: Routes
description: How HTTP methods map to controllers and actions.
menu:
docs:
weight: 40
parent: dev
---
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/config/routes.rb" caption="config/routes.rb" >}}
## Explanation of routes
Mastodon uses Ruby on Rails, which defines its router configuration at config/routes.rb. You may view the [Ruby on Rails routing guide](https://guides.rubyonrails.org/routing.html) for more detailed information, but this page will explain the basics of how Mastodon handles routing.
### How routes are constructed
`namespace` is a prefix for routes mapped to a certain controller directory. `resources` are mapped to controllers within that namespace directory. `scope` passes to the `module`'s controller. For example, consider the following abbreviated code:
{{< code title="config/routes.rb excerpt" >}}
```ruby
namespace :api do
namespace :v1 do
resources :statuses, only [:create, :show, :destroy] do
scope module: :statuses do
resource :favourite, only: :create
resource :unfavourite, to: 'favourites#destroy'
member do
get :context
end
end
end
end
end
```
{{< /code >}}
The first available resource is :statuses, which is nested under the :api and :v1 namespaces. Thus, the resulting HTTP route will be /api/v1/statuses. The `only` defines certain allowed methods, which are to be defined in the controller at `app/controllers/api/v1/statuses_controller.rb`.
Within /api/v1/statuses, there is a scope for a module :statuses, where additional resources are defined. The controllers for these resources live in `app/controllers/api/v1/statuses/`. For example, :favourite will be handled by the \#create action within `app/controllers/api/v1/statuses/favourites_controller.rb` and :unfavourite will be handled within the same controller, but by the \#destroy action.
There is also a custom method defined for any `member` within this scope, or in other words, for any status to be controlled by `app/controllers/api/v1/statuses_controller.rb`, which is mapped to GET /api/v1/statuses/:id/context and handled by the :context action defined within that controller.
### Available methods
#### :index
Maps to HTTP GET, for a list. Handled by the \#index action in a controller.
#### :show
Maps to HTTP GET, for a single view. Handled by the \#show action in a controller.
#### :create
Maps to HTTP POST. Handled by the \#create action in a controller.
#### :update
Maps to HTTP PUT. Handled by the \#update action in a controller.
#### :destroy
Maps to HTTP DELETE. Handled by the \#destroy action in a controller.
## .well-known
### /.well-known/host-meta
Extensible Resource Descriptor \(XRD\). Advertises existence of Webfinger.
### /.well-known/nodeinfo
Maps to NodeInfo 2.0 endpoint at `/nodeinfo/2.0`, used for advertising software name and version, protocols, usage statistics, and whether registrations are open.
### /.well-know/webfinger
Used for discovering ActivityPub actor id. See [Spec compliance &gt; WebFinger]({{< relref "../spec/webfinger.md" >}}) for more information.
### /.well-known/change-password
Maps to account settings page.
### /.well-known/keybase-proof-config
Used for integration with Keybase, defining which usernames are acceptable and where proofs may be checked.
{{< hint style="warning" >}}
The sections below this point are under construction.
{{< /hint >}}
## Public URIs
* `/users/username` = user URI
* `/users/username/remote_follow` = remote follow dialog
* `/users/username/statuses/id` = status URI
* `/@username` = "toots" tab
* `/@username/with_replies` = "toots and replies" tab
* `/@username/media` = "media" tab
* `/@username/tagged/:hashtag` = tagged statuses by user
* `/@username/:status_id` = status permalink
* `/@username/:status_id/embed` = embeddable version
* `/interact/:status_id` = remote interaction dialog
* `/explore` = profile directory
* `/explore/:hashtag` = profiles with this hashtag in bio
* `/public` = public timeline preview
* `/about` = landing page
* `/about/more` = extended description
* `/terms` = terms of service
## API
* /api/oembed
* /api/proofs
* /api/v1
* [statuses]({{< relref "../methods/statuses/" >}}) \[create, show, destroy\]
* reblogged\_by \[index\]
* favourited\_by \[index\]
* reblog \[create\]
* unreblog \[POST reblog\#destroy\]
* favourite \[create\]
* unfavourite \[POST favourites\#destroy\]
* bookmark \[create\]
* unbookmark \[POST bookmarks\#destroy\]
* mute \[create\]
* unmute \[POST mutes\#destroy\]
* pin \[create\]
* unpin \[POST pins\#destroy\]
* context \[GET\]
* [timelines]({{< relref "../methods/timelines/" >}})
* home \[show\]
* public \[show\]
* tag \[show\]
* list \[show\]
* [streaming]({{< relref "../methods/timelines/streaming.md" >}}) \[index\]
* [custom\_emojis]({{< relref "../methods/instance/custom_emojis.md" >}}) \[index\]
* [suggestions]({{< relref "../methods/accounts/suggestions.md" >}}) \[index, destroy\]
* [scheduled\_statuses]({{< relref "../methods/statuses/scheduled_statuses.md" >}}) \[index, show, update, destroy\]
* [preferences]({{< relref "../methods/accounts/preferences.md" >}}) \[index\]
* [conversations]({{< relref "../methods/timelines/conversations.md" >}}) \[index, destroy\]
* read \[POST\]
* [media]({{< relref "../methods/statuses/media.md" >}}) \[create, update\]
* [blocks]({{< relref "../methods/accounts/blocks.md" >}}) \[index\]
* [mutes]({{< relref "../methods/accounts/mutes.md" >}}) \[index\]
* [favourites]({{< relref "../methods/accounts/favourites.md" >}}) \[index\]
* [bookmarks]({{< relref "../methods/accounts/bookmarks.md" >}}) \[index\]
* [reports]({{< relref "../methods/accounts/reports.md" >}}) \[create\]
* [trends]({{< relref "../methods/instance/trends.md" >}}) \[index\]
* [filters]({{< relref "../methods/accounts/filters.md" >}}) \[index, create, show, update, destroy\]
* [endorsements]({{< relref "../methods/accounts/endorsements.md" >}}) \[index\]
* [markers]({{< relref "../methods/timelines/markers.md" >}}) \[index, create\]
* [apps]({{< relref "../methods/apps/" >}}) \[create\]
* verify\_credentials \[credentials\#show\]
* [instance]({{< relref "../methods/instance/" >}}) \[show\]
* peers \[index\]
* activity \[show\]
* [domain\_blocks]({{< relref "../methods/accounts/domain_blocks.md" >}}) \[show, create, destroy\]
* [directory]({{< relref "../methods/instance/directory.md" >}}) \[show\]
* [follow\_requests]({{< relref "../methods/accounts/follow_requests.md" >}}) \[index\]
* authorize \[POST\]
* reject \[POST\]
* [notifications]({{< relref "../methods/notifications/" >}}) \[index, show\]
* clear \[POST\]
* dismiss \[POST\]
* [accounts]({{< relref "../methods/accounts/" >}})
* verify\_credentials \[GET credentials\#show\]
* update\_credentials \[PATCH credentials\#update\]
* search \[show \(search\#index\)\]
* relationships \[index\]
* [accounts]({{< relref "../methods/accounts/" >}}) \[create, show\]
* statuses \[index accounts/statuses\]
* followers \[index accounts/follower\_accounts\]
* following \[index accounts/following\_accounts\]
* lists \[index accounts/lists\]
* identity\_proofs \[index accounts/identity\_proofs\]
* follow \[POST\]
* unfollow \[POST\]
* block \[POST\]
* unblock \[POST\]
* mute \[POST\]
* unmute \[POST\]
* pin \[POST\]
* unpin \[POST\]
* [lists]({{< relref "../methods/timelines/lists.md" >}}) \[index, create, show, update, destroy\]
* accounts \[POST accounts/pins\#destroy\]
* [featured\_tags]({{< relref "../methods/accounts/featured_tags.md" >}}) \[index, create, destroy\]
* suggestions \[GET suggestions\#index\]
* [polls]({{< relref "../methods/statuses/polls.md" >}}) \[create, show\]
* votes \[create polls/votes\]
* [push]({{< relref "../methods/notifications/push.md" >}})
* subscription \[create, show, update, destroy\]
* [admin]({{< relref "../methods/admin.md" >}})
* accounts \[index, show\]
* enable \[POST\]
* unsilence \[POST\]
* unsuspend \[POST\]
* approve \[POST\]
* reject \[POST\]
* action \[create account\_actions\]
* reports \[index, show\]
* assign\_to\_self \[POST\]
* unassign \[POST\]
* reopen \[POST\]
* resolve \[POST\]
* /api/v2
* [search]({{< relref "../methods/search.md" >}}) \[GET search\#index\]

38
content/en/dev/setup.md Normal file
View File

@ -0,0 +1,38 @@
---
title: Setting up a dev environment
menu:
docs:
weight: 20
parent: dev
---
{{< hint style="danger" >}}
This page is under construction.
{{< /hint >}}
### Setup <a id="setup"></a>
Run following commands in the project directory `bundle install`, `yarn install`.
In the development environment, Mastodon will use PostgreSQL as the currently signed-in Linux user using the `ident` method, which usually works out of the box. The one command you need to run is `rails db:setup` which will create the databases `mastodon_development` and `mastodon_test`, load the schema into them, and then create seed data defined in `db/seed.rb` in `mastodon_development`. The only seed data is an admin account with the credentials `admin@localhost:3000` / `mastodonadmin`.
> Please keep in mind, by default Mastodon will run on port 3000. If you configure a different port for it, the generated admin account will use that number.
### Running <a id="running"></a>
There are multiple processes that need to be run for the full set of Mastodons functionality, although they can be selectively omitted. To run all of them with just one command, you can install Foreman with `gem install foreman --no-document` and then use:
```text
foreman start
```
In the Mastodon directory. This will start processes defined in `Procfile.dev`, which will give you: A Rails server, a Webpack server, a streaming API server, and Sidekiq. Of course, you can run any of those things stand-alone depending on your needs.
### Testing <a id="testing"></a>
| Command | Description |
| :--- | :--- |
| `rspec` | Run the Ruby test suite |
| `yarn run test` | Run the JavaScript test suite |
| `rubocop` | Check the Ruby code for conformance with our code style |

View File

@ -1,136 +0,0 @@
---
title: ActivityPub compliance
description: What objects and properties of the ActivityPub spec Mastodon supports
menu:
docs:
parent: development
weight: 5
---
## APIs
- Mastodon supports the server-to-server part of the [ActivityPub spec](https://www.w3.org/TR/activitypub/).
- It implements the [HTTP signatures spec](https://tools.ietf.org/html/draft-cavage-http-signatures-10) for authentication of inbox deliveries.
- Mastodon also supports [Linked Data Signatures](https://w3c-dvcg.github.io/ld-signatures/) for forwarded payloads.
## Restrictions
- All object IDs must use the `https://` schema.
- Servers must offer a [WebFinger](https://tools.ietf.org/html/rfc7033) endpoint for turning usernames into actors.
- Activities attributed to an actor must have an ID on the same host as the actor.
## Activities
|Supported activity|Supported objects|
|------------------|-----------------|
|`Accept`|`Follow`|
|`Add`|`Note`|
|`Announce`|`Object`|
|`Block`|`Object`|
|`Create`|`Note`, `Article`, `Image`, `Video`, `Page`|
|`Delete`|`Object`|
|`Flag`|`Object`|
|`Follow`|`Object`|
|`Like`|`Object`|
|`Move`|`Object`|
|`Reject`|`Follow`|
|`Remove`|`Note`|
|`Undo`|`Accept`, `Announce`, `Block`, `Follow`, `Like`|
|`Update`|`Object`|
As far as the `Create` activity is concerned, only `Note` is a first-class citizen in Mastodon, because Mastodon is a microblogging engine. For other types of supported objects, Mastodon internally creates a toot representation, for example, an `Article` or `Page` becomes a toot with the `name` and `url` of the original object, as users are expected to navigate to the original URL to read the article with rich text formatting. For `Image` and `Video` objects, the `name` is likewise used to fill the content of the toot, with the original file attached to the toot.
The `Flag` activity allows reporting content on another server, and its `object` can be either one or more actors, or one or more objects attributed to various actors. The `Add` and `Remove` activities only work with [featured collections](#featured-collection). The `Delete` activity can be used to delete all local data of the sender when the `object` of it is the sender. The `Update` activity can only be used to update the profile of the sender. Likewise, the `Move` activity allows re-assigning followers from the sender (`object`) to another actor (`target`), but only if the other actor references the sender in the `alsoKnownAs` property.
## Extensions
### Featured collection
What is known in Mastodon as "pinned toots", or statuses that are always featured at the top of people's profiles, is implemented using an extra property `featured` on the actor object that points to a `Collection` of objects. Example:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"featured": {
"@id": "toot:featured",
"@type": "@id"
}
}
],
"id": "https://example.com/@alice",
"type": "Person",
"featured": "https://example.com/@alice/collections/featured"
}
```
### Custom emojis
Mastodon supports arbitrary emojis, that is, small images uploaded by admins and invokable via shortcodes. For this, an `Emoji` type is used. These emojis are listed in the `tag` property just like `Mention` and `Hashtag` objects, since they are entities that affect how the text is rendered. Example:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"Emoji": "toot:Emoji"
}
],
"id": "https://example.com/@alice/hello-world",
"type": "Note",
"content": "Hello world :Kappa:",
"tag": [
{
"id": "https://example.com/emoji/123",
"type": "Emoji",
"name": ":Kappa:",
"icon": {
"type": "Image",
"mediaType": "image/png",
"url": "https://example.com/files/kappa.png"
}
}
]
}
```
### Focal points
Mastodon supports setting a focal point on uploaded images, so that wherever that image is displayed, the focal point stays in view. This is implemented using an extra property `focalPoint` on the `Image` objects. The property is simply an array of two floating points between 0 and 1. Example:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://example.com/@alice/hello-world",
"type": "Note",
"content": "A picture attached!",
"attachment": [
{
"type": "Image",
"mediaType": "image/png",
"url": "https://example.com/files/cats.png",
"focalPoint": [
0.55,
0.43
]
}
]
}
```

View File

@ -1,118 +0,0 @@
---
title: Overview
description: How to setup a development environment for Mastodon
menu:
docs:
parent: development
weight: 1
---
Mastodon is a **Ruby on Rails** application with a **React.js** front-end. It follows standard practices of those frameworks, so if you are already familiar with Rails or React.js, you will not find any surprises here.
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.
## 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.
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:
- Ruby code reloads itself when you change it, which means you don't need to restart the Rails server process to see changes
- All errors you encounter show stack traces in the browser, rather than being hidden behind a generic error page
- Webpack runs continuously and re-compiles JS and CSS assets when you change any of the front-end files, and the pages automatically reload
- Caching is disabled by default
- An admin account with the e-mail `admin@localhost:3000` and password `mastodonadmin` is created automatically during `db:seed`
It should be noted that the Docker configuration distributed with Mastodon is optimized for the production environment, and so is an extremely bad fit for development. The Vagrant configuration, on the other hand, is meant specifically for development and not production use.
## Setup
Run following commands in the project directory `bundle install`, `yarn install`.
In the development environment, Mastodon will use PostgreSQL as the currently signed-in Linux user using the `ident` method, which usually works out of the box. The one command you need to run is `rails db:setup` which will create the databases `mastodon_development` and `mastodon_test`, load the schema into them, and then create seed data defined in `db/seed.rb` in `mastodon_development`. The only seed data is an admin account with the credentials `admin@localhost:3000` / `mastodonadmin`.
> Please keep in mind, by default Mastodon will run on port 3000. If you configure a different port for it, the generated admin account will use that number.
## Running
There are multiple processes that need to be run for the full set of Mastodon's functionality, although they can be selectively omitted. To run all of them with just one command, you can install Foreman with `gem install foreman --no-document` and then use:
foreman start
In the Mastodon directory. This will start processes defined in `Procfile.dev`, which will give you: A Rails server, a Webpack server, a streaming API server, and Sidekiq. Of course, you can run any of those things stand-alone depending on your needs.
## Testing
|Command|Description|
|-------|-----------|
|`rspec`|Run the Ruby test suite|
|`yarn run test`|Run the JavaScript test suite|
|`rubocop`|Check the Ruby code for conformance with our code style|
## Most notable libraries used
Knowledge and understanding of these libraries will simplify work with the Mastodon code.
### Ruby
- `haml`, a templating language
- `devise`, for authentication
- `doorkeeper`, for acting as an OAuth 2 provider
- `paperclip`, for file uploads and attachments
- `sidekiq`, for background processing
### JavaScript
- `immutable`, for immutable data structures
- `react`, for rendering the dynamic web application
- `react-redux`, for managing React state
- `react-router-dom`, for navigation within React
- `react-intl`, for localizations within React
## Code 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.
### Ruby
|Path|Description|
|----|-----------|
|`app/controllers`|Code that binds business logic to templates|
|`app/helpers`|Code that can be used from views, i.e. common operations|
|`app/lib`|Code that doesn't fit in the other categories|
|`app/models`|Code that represents data entities|
|`app/serializers`|Code that generates JSON from models|
|`app/services`|Complex logical operations involving multiple models|
|`app/views`|Templates for generating HTML or other output|
|`app/workers`|Code that executes outside the request-response cycle|
|`spec`|Automated test suite|
### JavaScript
|Path|Description|
|----|-----------|
|`app/javascript/mastodon`|Code for the multi-column React.js application|
|`app/javascript/packs`|Code for non-React.js pages|
### CSS and other assets
|Path|Description|
|----|-----------|
|`app/javascript/images`|Images|
|`app/javascript/styles`|Code that turns into CSS via Sass|
### Localizations
|Path|Description|
|----|-----------|
|`config/locales`|Server-side localizations in the YML format|
|`app/javascript/mastodon/locales`|Client-side localizations in the JSON format|
## Localization maintenance
All locale files are normalized to ensure consistent formatting and key order, which minimizes changesets in version control.
|Command|Description|
|-------|-----------|
|`i18n-tasks normalize`|Normalize server-side translations|
|`yarn run manage:translations`|Normalize client-side translations|

View File

@ -0,0 +1,221 @@
---
title: Account
description: Represents a user of Mastodon and their associated profile.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "23634",
"username": "noiob",
"acct": "noiob@awoo.space",
"display_name": "ikea shark fan account",
"locked": false,
"bot": false,
"created_at": "2017-02-08T02:00:53.274Z",
"note": "<p>:ms_rainbow_flag: :ms_bisexual_flagweb: :ms_nonbinary_flag: <a href=\"https://awoo.space/tags/awoo\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>awoo</span></a>.space <a href=\"https://awoo.space/tags/admin\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>admin</span></a> ~ <a href=\"https://awoo.space/tags/bi\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>bi</span></a> ~ <a href=\"https://awoo.space/tags/nonbinary\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>nonbinary</span></a> ~ compsci student ~ likes video <a href=\"https://awoo.space/tags/games\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>games</span></a> and weird/ old electronics and will post obsessively about both ~ avatar by <span class=\"h-card\"><a href=\"https://weirder.earth/@dzuk\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>dzuk</span></a></span></p>",
"url": "https://awoo.space/@noiob",
"avatar": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"header": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"followers_count": 547,
"following_count": 404,
"statuses_count": 28468,
"last_status_at": "2019-11-17T00:02:23.693Z",
"emojis": [
{
"shortcode": "ms_rainbow_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/028/691/original/6de008d6281f4f59.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/028/691/static/6de008d6281f4f59.png",
"visible_in_picker": true
},
{
"shortcode": "ms_bisexual_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/050/744/original/02f94a5fca7eaf78.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/050/744/static/02f94a5fca7eaf78.png",
"visible_in_picker": true
},
{
"shortcode": "ms_nonbinary_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/105/099/original/8106088bd4782072.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/105/099/static/8106088bd4782072.png",
"visible_in_picker": true
}
],
"fields": [
{
"name": "Pronouns",
"value": "they/them",
"verified_at": null
},
{
"name": "Alt",
"value": "<span class=\"h-card\"><a href=\"https://cybre.space/@noiob\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>noiob</span></a></span>",
"verified_at": null
},
{
"name": "Bots",
"value": "<span class=\"h-card\"><a href=\"https://botsin.space/@darksouls\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>darksouls</span></a></span>, <span class=\"h-card\"><a href=\"https://botsin.space/@nierautomata\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>nierautomata</span></a></span>, <span class=\"h-card\"><a href=\"https://mastodon.social/@fedi\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>fedi</span></a></span>, code for <span class=\"h-card\"><a href=\"https://botsin.space/@awoobot\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>awoobot</span></a></span>",
"verified_at": null
},
{
"name": "Website",
"value": "<a href=\"http://shork.xyz\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">http://</span><span class=\"\">shork.xyz</span><span class=\"invisible\"></span></a>",
"verified_at": "2019-11-10T10:31:10.744+00:00"
}
]
}
```
## Base attributes
### **`id`**
**Description:** The account id`header`.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 0.1.0
### `username`
**Description:** The username of the account, not including domain.
**Type:** String
**Version history:** Added in 0.1.0
### `acct`
**Description:** The Webfinger account URI.
Equal to `username` for local users, or `username@domain` for remote users.
**Type:** String
**Version history:** Added in 0.1.0
### `url`
**Description:** The location of the user's profile page.
**Type:** String \(HTTPS URL\)
**Version history:** Added in 0.1.0
## Display attributes
### `display_name`
**Description:** The profile's display name.
**Type:** String
**Version history:** Added in 0.1.0
### `note`
**Description:** The profile's bio / description.
**Type:** String
**Version history:** Added in 0.1.0
### `avatar`
**Description:** An image icon that is shown next to statuses and in the profile.
**Type:** String \(URL\)
**Version history:** Added in 0.1.0
### `avatar_static`
**Description:** A static version of the avatar.
Equal to `avatar` if its value is a static image; different if `avatar` is an animated GIF.
**Type:** String \(URL\)
**Version history:** Added in 1.1.2
### `header`
**Description:** An image banner that is shown above the profile and in profile cards.
**Type:** String \(URL\)
**Version history:** Added in 0.1.0
### `header_static`
**Description:** A static version of the header.
Equal to `header` if its value is a static image; different if `header` is an animated GIF.
**Type:** String \(URL\)
**Version history:** Added in 1.1.2
### `locked`
**Description:** Whether the account manually approves follow requests.
**Type:** Boolean
**Version history:** Added in 0.1.0
### `emojis`
**Description:** Custom emoji entities to be used when rendering the profile. If none, an empty array will be returned.
**Type:** Array of [Emoji](emoji.md)
**Version history:** Added in 2.4.0
### `discoverable`
**Description:** Whether the account has opted into discovery features such as the profile directory.
**Type:** Boolean
**Version history:** Added in 3.1.0
## Statistical attributes
### `created_at`
**Description:** When the account was created.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 0.1.0
### `statuses_count`
**Description:** How many statuses are attached to this account.
**Type:** Number
**Version history:** Added in 0.1.0
### `followers_count`
**Description:** The reported followers of this profile.
**Type:** Number
**Version history:** Added in 0.1.0
### `following_count`
**Description:** The reported follows of this profile.
**Type:** Number
**Version history:** Added in 0.1.0
## Optional attributes
### `moved`
**Description:** Indicates that the profile is currently inactive and that its user has moved to a new account.
**Type:** [Account](account.md)
**Version history:** Added in 2.1.0
### `fields`
**Description:** Additional metadata attached to a profile as name-value pairs.
**Type:** Array of [Field]({{< relref "field.md" >}})
**Version history:** Added in 2.4.0
### `bot`
**Description:** A presentational flag. Indicates that the account may perform automated actions, may not be monitored, or identifies as a robot.
**Type:** Boolean
**Version history:** Added in 2.4.0
### `source`
**Description:** An extra entity to be used with API methods to [verify credentials](../methods/accounts/#verify-account-credentials) and [update credentials](../methods/accounts/#update-account-credentials).
**Type:** [Source](source.md)
**Version history:** Added in 2.4.0
## See also
{{< page-ref page="methods/accounts.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/account_serializer.rb" caption="app/serializers/rest/account\_serializer.rb" >}}

View File

@ -0,0 +1,55 @@
---
title: Activity
description: Represents a weekly bucket of instance activity.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"week": "1574640000",
"statuses": "37125",
"logins": "14239",
"registrations": "542"
}
```
## Attributes
### `week`
**Description:** Midnight at the first day of the week.
**Type:** String \(UNIX Timestamp\)
**Version history:** Added in 2.1.2
### `statuses`
**Description:** Statuses created since the week began.
**Type:** String \(cast from an integer\)
**Version history:** Added in 2.1.2
### `logins`
**Description:** User logins since the week began.
**Type:** String \(cast from an integer\)
**Version history:** Added in 2.1.2
### `registrations`
**Description:** User registrations since the week began.
**Type:** String \(cast from an integer\)
**Version history:** Added in 2.1.2
## See also
* [GET /api/v1/instance/activity](../methods/instance/#weekly-activity)
{{< page-ref page="methods/instance.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/controllers/api/v1/instances/activity_controller.rb" caption="app/controllers/api/v1/instances/activity\_controller.rb" >}}

View File

@ -0,0 +1,130 @@
---
title: Admin::Account
description: Admin-level information about a given account.
menu:
docs:
parent: entities
---
## Example
```javascript
{}
```
## Attributes
### `id`
**Description:** The ID of the account in the database.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.9.1
### `username`
**Description:** The username of the account.
**Type:** String
**Version history:** Added in 2.9.1
### `domain`
**Description:** The domain of the account.
**Type:** String
**Version history:** Added in 2.9.1
### `created_at`
**Description:** When the account was first discovered.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 2.9.1
### `email`
**Description:** The email address associated with the account.
**Type:** String
**Version history:** Added in 2.9.1
### `ip`
**Description:** The IP address last used to login to this account.
**Type:** String
**Version history:** Added in 2.9.1
### `locale`
**Description:** The locale of the account.
**Type:** String \(ISO 639 Part 1 two-letter language code\)
**Version history:** Added in 2.9.1
### `invite_request`
**Description:** Invite request text ???
**Type:** String
**Version history:** Added in 2.9.1
## State attributes
### `role`
**Description:** The current role of the account.
**Type:** String \(Enumerable oneOf\)
**Version history:** Added in 2.9.1
### `confirmed`
**Description:** Whether the account has confirmed their email address.
**Type:** Boolean
**Version history:** Added in 2.9.1
### `approved`
**Description:** Whether the account is currently approved.
**Type:** Boolean
**Version history:** Added in 2.9.1
### `disabled`
**Description:** Whether the account is currently disabled.
**Type:** Boolean
**Version history:** Added in 2.9.1
### `silenced`
**Description:** Whether the account is currently silenced.
**Type:** Boolean
**Version history:** Added in 2.9.1
### `suspended`
**Description:** Whether the account is currently suspended.
**Type:** Boolean
**Version history:** Added in 2.9.1
### `account`
**Description:** User-level information about the account.
**Type:** Account
**Version history:** Added in 2.9.1
## Nullable attributes
### `created_by_application_id`
**Description:** The ID of the application that created this account.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.9.1
### `invited_by_account_id`
**Description:** The ID of the account that invited this user
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.9.1
## See also
{{< page-ref page="methods/admin.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/admin/account_serializer.rb" caption="app/serializers/rest/admin/account\_serializer.rb" >}}

View File

@ -0,0 +1,84 @@
---
title: Admin::Report
description: Admin-level information about a filed report.
menu:
docs:
parent: entities
---
## Example
```javascript
{}
```
## Attributes
### `id`
**Description:** The ID of the report in the database.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.9.1
### `action_taken`
**Description:** The action
**Type:** String \(Enumerable oneOf\)
**Version history:** Added in 2.9.1
### `comment`
**Description:** An optional reason for reporting.
**Type:** String
**Version history:** Added in 2.9.1
### `created_at`
**Description:** The time the report was filed.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 2.9.1
### `updated_at`
**Description:** The time of last action on this report.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 2.9.1
### `account`
**Description:** The account which filed the report.
**Type:** [Account](account.md)
**Version history:** Added in 2.9.1
### `target_account`
**Description:** The account being reported.
**Type:** [Account](account.md)
**Version history:** Added in 2.9.1
### `assigned_account`
**Description:** The account of the moderator assigned to this report.
**Type:** [Account](account.md)
**Version history:** Added in 2.9.1
### `action_taken_by_account`
**Description:** The action taken by the moderator who handled the report.
**Type:** String \(Enumerable oneOf\)
**Version history:** Added in 2.9.1
### `statuses`
**Description:** Statuses attached to the report, for context.
**Type:** Array of [Status](status.md)
**Version history:** Added in 2.9.1
## See also
{{< page-ref page="methods/admin.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/admin/report_serializer.rb" caption="app/serializers/rest/admin/report\_serializer.rb" >}}

View File

@ -0,0 +1,69 @@
---
title: Application
description: >-
Represents an application that interfaces with the REST API to access accounts
or post statuses.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"name": "test app",
"website": null,
"vapid_key": "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M="
}
```
## Required attributes
### `name`
**Description:** The name of your application.
**Type:** String
**Version history:** Added in 0.9.9
## Optional attributes
### `website`
**Description:** The website associated with your application.
**Type:** String \(URL\)
**Version history:** Added in 0.9.9
### `vapid_key`
**Description:** Used for Push Streaming API. Returned with [POST /api/v1/apps](../methods/apps/#create-an-application). Equivalent to [PushSubscription\#server\_key](push-subscription.md#server_key)
**Type:** String
**Version history:** Added in 2.8.0
## Client attributes
### client\_id
**Description:** Client ID key, to be used for obtaining OAuth tokens
**Type:** String
**Version history:** Added in 0.9.9
### client\_secret
**Description:** Client secret key, to be used for obtaining OAuth tokens
**Type:** String
**Version history:** Added in 0.9.9
## See also
* [Status\#application](status.md#application)
* [Create an application \(POST /api/v1/apps\)](../methods/apps/#create-an-application)
{{< page-ref page="status.md" >}}
{{< page-ref page="methods/apps.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/application_serializer.rb" caption="app/serializers/rest/application\_serializer.rb" >}}

View File

@ -0,0 +1,230 @@
---
title: Attachment
description: Represents a file or media attachment that can be added to a status.
menu:
docs:
parent: entities
---
## Example
{{< tabs >}}
{{< tab title="Image" >}}
```javascript
{
"id": "22345792",
"type": "image",
"url": "https://files.mastodon.social/media_attachments/files/022/345/792/original/57859aede991da25.jpeg",
"preview_url": "https://files.mastodon.social/media_attachments/files/022/345/792/small/57859aede991da25.jpeg",
"remote_url": null,
"text_url": "https://mastodon.social/media/2N4uvkuUtPVrkZGysms",
"meta": {
"original": {
"width": 640,
"height": 480,
"size": "640x480",
"aspect": 1.3333333333333333
},
"small": {
"width": 461,
"height": 346,
"size": "461x346",
"aspect": 1.3323699421965318
},
"focus": {
"x": -0.27,
"y": 0.51
}
},
"description": "test media description",
"blurhash": "UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}"
}
```
{{< endtab >}}
{{< tab title="Video" >}}
```javascript
{
"id": "22546306",
"type": "video",
"url": "https://files.mastodon.social/media_attachments/files/022/546/306/original/dab9a597f68b9745.mp4",
"preview_url": "https://files.mastodon.social/media_attachments/files/022/546/306/small/dab9a597f68b9745.png",
"remote_url": null,
"text_url": "https://mastodon.social/media/wWd1HJIBmH1MZGDfg50",
"meta": {
"length": "0:01:28.65",
"duration": 88.65,
"fps": 24,
"size": "1280x720",
"width": 1280,
"height": 720,
"aspect": 1.7777777777777777,
"audio_encode": "aac (LC) (mp4a / 0x6134706D)",
"audio_bitrate": "44100 Hz",
"audio_channels": "stereo",
"original": {
"width": 1280,
"height": 720,
"frame_rate": "6159375/249269",
"duration": 88.654,
"bitrate": 862056
},
"small": {
"width": 400,
"height": 225,
"size": "400x225",
"aspect": 1.7777777777777777
}
},
"description": null,
"blurhash": "U58E0g8_0M.94T?bIr00?bD%NGoM?bD%oLt7"
}
```
{{< endtab >}}
{{< tab title="GIFV" >}}
```javascript
{
"id": "21130559",
"type": "gifv",
"url": "https://files.mastodon.social/media_attachments/files/021/130/559/original/bc84838f77991326.mp4",
"preview_url": "https://files.mastodon.social/media_attachments/files/021/130/559/small/bc84838f77991326.png",
"remote_url": null,
"text_url": "https://mastodon.social/media/2ICiasGyjezmi7cQYM8",
"meta": {
"length": "0:00:01.11",
"duration": 1.11,
"fps": 33,
"size": "600x332",
"width": 600,
"height": 332,
"aspect": 1.8072289156626506,
"original": {
"width": 600,
"height": 332,
"frame_rate": "100/3",
"duration": 1.11,
"bitrate": 1627639
},
"small": {
"width": 400,
"height": 221,
"size": "400x221",
"aspect": 1.8099547511312217
}
},
"description": null,
"blurhash": "URHT%Jm,2a1d%MRO%LozkrNH$*n*oMn$Rjt7"
}
```
{{< endtab >}}
{{< tab title="Audio" >}}
```javascript
{
"id": "21165404",
"type": "audio",
"url": "https://files.mastodon.social/media_attachments/files/021/165/404/original/a31a4a46cd713cd2.mp3",
"preview_url": "https://files.mastodon.social/media_attachments/files/021/165/404/small/a31a4a46cd713cd2.mp3",
"remote_url": null,
"text_url": "https://mastodon.social/media/5O4uILClVqBWx0NNgvo",
"meta": {
"length": "0:06:42.86",
"duration": 402.86,
"audio_encode": "mp3",
"audio_bitrate": "44100 Hz",
"audio_channels": "stereo",
"original": {
"duration": 402.860408,
"bitrate": 166290
}
},
"description": null,
"blurhash": null
}
```
{{< endtab >}}
{{< endtabs >}}
## Required attributes
### `id`
**Description:** The ID of the attachment in the database.
**Type:** String \(cast from an integer but not guaranteed to be a number\)
**Version history:** Added in 0.6.0.
### `type`
**Description:** The type of the attachment.
**Type:** String \(Enumerable, oneOf\)
- `unknown` = unsupported or unrecognized file type
- `image` = Static image
- `gifv` = Looping, soundless animation
- `video` = Video clip
- `audio` = Audio track
**Version history:** Added in 0.6.0. Audio added in 2.9.1.
### `url`
**Description:** The location of the original full-size attachment.
**Type:** String \(URL\)
**Version history:** Added in 0.6.0.
### `preview_url`
**Description:** The location of a scaled-down preview of the attachment.
**Type:** String \(URL\)
**Version history:** Added in 0.6.0.
## Optional attributes
### `remote_url`
**Description:** The location of the full-size original attachment on the remote website.
**Type:** String \(URL\), or null if the attachment is local
**Version history:** Added in 0.6.0.
### `text_url`
**Description:** A shorter URL for the attachment.
**Type:** String \(URL\)
**Version history:** Added in 0.6.0.
### `meta`
**Description:** Metadata returned by Paperclip.
**Type:** Hash
**Version history:** Added in 1.5.0. meta\[focus\] added in 2.3.0.
May contain subtrees `small` and `original`, as well as various other top-level properties.
More importantly, there may be another top-level `focus` Hash object as of 2.3.0, with coordinates can be used for smart thumbnail cropping -- see [Focal points](../methods/statuses/media.md#focal-points) for more.
### `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
**Version history:** Added in 2.0.0
### `blurhash`
**Description:** A hash computed by [the BlurHash algorithm](https://github.com/woltapp/blurhash), for generating colorful preview thumbnails when media has not been downloaded yet.
**Type:** String
**Version history:** Added in 2.8.1
## See also
* Status\#media\_attachments
* /api/v1/media
{{< page-ref page="status.md" >}}
{{< page-ref page="methods/statuses/media.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/media_attachment_serializer.rb" caption="app/serializers/rest/media\_attachment\_serializer.rb" >}}

168
content/en/entities/card.md Normal file
View File

@ -0,0 +1,168 @@
---
title: Card
description: >-
Represents a rich preview card that is generated using OpenGraph tags from a
URL.
menu:
docs:
parent: entities
---
{{< tabs >}}
{{< tab title="video" >}}
```javascript
{
"url": "https://www.youtube.com/watch?v=OMv_EPMED8Y",
"title": "♪ Brand New Friend (Christmas Song!)",
"description": "",
"type": "video",
"author_name": "YOGSCAST Lewis & Simon",
"author_url": "https://www.youtube.com/user/BlueXephos",
"provider_name": "YouTube",
"provider_url": "https://www.youtube.com/",
"html": "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/OMv_EPMED8Y?feature=oembed\" frameborder=\"0\" allowfullscreen=\"\"></iframe>",
"width": 480,
"height": 270,
"image": "https://files.mastodon.social/preview_cards/images/014/179/145/original/9cf4b7cf5567b569.jpeg",
"embed_url": ""
}
```
{{< endtab >}}
{{< tab title="photo" >}}
```javascript
{
"url": "https://www.flickr.com/photos/tomfenskephotography/49088768431/",
"title": "Oregon",
"description": "",
"type": "photo",
"author_name": "Tom Fenske Photography",
"author_url": "https://www.flickr.com/photos/tomfenskephotography/",
"provider_name": "Flickr",
"provider_url": "https://www.flickr.com/",
"html": "",
"width": 1024,
"height": 427,
"image": "https://files.mastodon.social/preview_cards/images/014/287/139/original/651b1c6976817824.jpeg",
"embed_url": "https://live.staticflickr.com/65535/49088768431_6a4322b3bb_b.jpg"
}
```
{{< endtab >}}
{{< tab title="link" >}}
```javascript
{
"url": "https://www.theguardian.com/money/2019/dec/07/i-lost-my-193000-inheritance-with-one-wrong-digit-on-my-sort-code",
"title": "I lost my £193,000 inheritance with one wrong digit on my sort code",
"description": "When Peter Teichs money went to another Barclays customer, the bank offered £25 as a token gesture",
"type": "link",
"author_name": "",
"author_url": "",
"provider_name": "",
"provider_url": "",
"html": "",
"width": 0,
"height": 0,
"image": null,
"embed_url": ""
}
```
{{< endtab >}}
{{< endtabs >}}
## Required attributes
### `url`
**Description:** Location of linked resource.
**Type:** String \(URL\)
**Version history:** Added in 1.0.0
### `title`
**Description:** Title of linked resource.
**Type:** String
**Version history:** Added in 1.0.0
### `description`
**Description:** Description of preview.
**Type:** String
**Version history:** Added in 1.0.0
### `type`
**Description:** The type of the preview card.
**Type:** String \(Enumerable, oneOf\)
- `link` = Link OEmbed
- `photo` = Photo OEmbed
- `video` = Video OEmbed
- `rich` = iframe OEmbed. Not currently accepted, so won't show up in practice.
**Version history:** Added in 1.3.0
## Optional attributes
### `author_name`
**Description:** The author of the original resource.
**Type:** String
**Version history:** Added in 1.3.0
### `author_url`
**Description:** A link to the author of the original resource.
**Type:** String \(URL\)
**Version history:** Added in 1.3.0
### `provider_name`
**Description:** The provider of the original resource.
**Type:** String
**Version history:** Added in 1.3.0
### `provider_url`
**Description:** A link to the provider of the original resource.
**Type:** String \(URL\)
**Version history:** Added in 1.3.0
### `html`
**Description:** HTML to be used for generating the preview card.
**Type:** String \(HTML\)
**Version history:** Added in 1.3.0
### `width`
**Description:** Width of preview, in pixels.
**Type:** Number
**Version history:** Added in 1.3.0
### `height`
**Description:** Height of preview, in pixels.
**Type:** Number
**Version history:** Added in 1.3.0
### `image`
**Description:** Preview thumbnail.
**Type:** String \(URL\)
**Version history:** Added in 1.0.0
### `embed_url`
**Description:** Used for photo embeds, instead of custom `html`.
**Type:** String \(URL\)
**Version history:** Added in 2.1.0
## See also
{{< page-ref page="status.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/preview_card_serializer.rb" caption="app/serializers/rest/preview\_card\_serializer.rb" >}}
### <a id="type-1"></a>

View File

@ -0,0 +1,70 @@
---
title: Context
description: >-
Represents the tree around a given status. Used for reconstructing threads of
statuses.
menu:
docs:
parent: entities
---
## Example
{{< code title="Truncated response from GET statuses/:id/context" >}}
```javascript
{
"ancestors": [
{
"id": "103188938570975982",
"created_at": "2019-11-23T19:44:00.124Z",
"in_reply_to_id": null,
...
},
{
"id": "103188971072973252",
"created_at": "2019-11-23T19:52:23.398Z",
"in_reply_to_id": "103188938570975982",
...
},
{
"id": "103188982235527758",
"created_at": "2019-11-23T19:55:08.208Z",
"in_reply_to_id": "103188971072973252",
...
],
"descendants": [
{
"id": "103189026958574542",
"created_at": "2019-11-23T20:06:36.011Z",
"in_reply_to_id": "103189005915505698",
...
}
]
}
```
{{< /code >}}
## Required attributes
### `ancestors`
**Description:** Parents in the thread.
**Type:** Array of [Status](status.md)
**Version history:** Added in 0.6.0
### `descendants`
**Description:** Children in the thread.
**Type:** Array of [Status](status.md)
**Version history:** Added in 0.6.0
## See also
* [GET /api/v1/statuses/:id/context](../methods/statuses/#parent-and-child-statuses)
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/context_serializer.rb" caption="app/serializers/rest/context\_serializer.rb" >}}

View File

@ -0,0 +1,70 @@
---
title: Conversation
description: Represents a conversation with "direct message" visibility.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "418450",
"unread": true,
"accounts": [
{
"id": "482403",
"username": "amic",
"acct": "amic@nulled.red",
...
}
],
"last_status": {
"id": "103196583826321184",
"created_at": "2019-11-25T04:08:24.000Z",
"in_reply_to_id": "103196540587943467",
"in_reply_to_account_id": "14715",
...
}
}
```
## Required attributes
### `id`
**Description:** Local database ID of the conversation.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.6.0
### `accounts`
**Description:** Participants in the conversation.
**Type:** Array of [Account](account.md)
**Version history:** Added in 2.6.0
### `unread`
**Description:** Is the conversation currently marked as unread?
**Type:** Boolean
**Version history:** Added in 2.6.0
## Optional attributes
### `last_status`
**Description:** The last status in the conversation, to be used for optional display.
**Type:** [Status](status.md)
**Version history:** Added in 2.6.0
## See also
{{< page-ref page="methods/timelines/conversations.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/conversation_serializer.rb" caption="app/serializers/rest/conversation\_serializer.rb" >}}

View File

@ -0,0 +1,68 @@
---
title: Emoji
description: Represents a custom emoji.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"shortcode": "blobaww",
"url": "https://files.mastodon.social/custom_emojis/images/000/011/739/original/blobaww.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/011/739/static/blobaww.png",
"visible_in_picker": true,
"category": "Blobs"
}
```
## Required attributes
### `shortcode`
**Description:** The name of the custom emoji.
**Type:** String
**Version history:** Added in 2.0.0
### `url`
**Description:** A link to the custom emoji.
**Type:** String \(URL\)
**Version history:** Added in 2.0.0
### `static_url`
**Description:** A link to a static copy of the custom emoji.
**Type:** String \(URL\)
**Version history:** Added in 2.0.0
### `visible_in_picker`
**Description:** Whether this Emoji should be visible in the picker or unlisted.
**Type:** Boolean
**Version history:** Added in 2.0.0
## Optional attributes
### `category`
**Description:** Used for sorting custom emoji in the picker.
**Type:** String
**Version history:** Added in 3.0.0
## See also
* [Status\#emojis](status.md#emojis)
{{< page-ref page="status.md" >}}
{{< page-ref page="methods/instance/custom_emojis.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/custom_emoji_serializer.rb" caption="app/serializers/rest/custom\_emoji\_serializer.rb" >}}

View File

@ -0,0 +1,87 @@
---
title: Error
description: Represents an error message.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"error": "invalid_grant",
"error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}
```
{{< hint style="info" >}}
**The most important part of an error response is the HTTP status code.** Standard semantics are followed. The body of an error is a JSON object containing more information, if available.
{{< /hint >}}
## Required attributes
### `error`
**Description:** The error message.
**Type:** String
**Version history:** Added in 0.6.0
## Optional attributes
### `error_description`
**Description:** A longer description of the error, mainly provided with the OAuth API.
**Type:** String
**Version history:** Added in 0.6.0
## Possible reasons
### 401 - Unauthorized
#### require\_authenticated\_user!
Error: This API requires an authenticated user. Appears when the instance is in secure mode, which disables all public use of API methods.
### 403 - Forbidden
#### current\_user.disabled?
Error: Your login is currently disabled. Appears when the OAuth token's authorized user has had their account disabled by a moderator.
#### !current\_user.confirmed?
Error: Your login is missing a confirmed e-mail address. Appears when the email address associated with the OAuth token's authorized user's account has not yet been confirmed.
#### !current\_user.approved?
Error: Your login is currently pending approval. Appears when the OAuth token's authorized user has signed up on an instance with approval-required registrations, and the user has not yet had their account approved by a moderator.
### 404 - Not Found
#### RecordNotFound
Error: Record not found. Appears when an entity record does not exist, or the authorized user is not within the audience of a private entity.operates on a user.
### 422 - Unprocessable Entity
#### RecordInvalid
Error: {string}. May appear when entity creation failed.
#### RecordNotUnique
Error: Duplicate record. Appears when you are trying to pin an account or status that is already pinned.
#### !current\_user
Error: This method requires an authenticated user. Appears when using an OAuth token without an authorized user \(or no token at all\), while trying to call an API method that requires a user to be processed.
## See also
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/controllers/api/base_controller.rb" caption="app/controllers/api/base\_controller.rb" >}}

View File

@ -0,0 +1,54 @@
---
title: FeaturedTag
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "627",
"name": "nowplaying",
"statuses_count": 36,
"last_status_at": "2019-11-15T07:14:43.524Z"
}
```
## Attributes
### `id`
**Description:** The internal ID of the featured tag in the database.
**Type:** String \(cast from integer but not guaranteed to be a number\)
**Version history:** Added in 3.0.0
### `name`
**Description:** The name of the hashtag being featured.
**Type:** String
**Version history:** Added in 3.0.0
### `statuses_count`
**Description:** The number of authored statuses containing this hashtag.
**Type:** Number
**Version history:** Added in 3.0.0
### `last_status_at`
**Description:** The timestamp of the last authored status containing this hashtag.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 3.0.0
## See also
{{< page-ref page="methods/accounts/featured_tags.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/featured_tag_serializer.rb" caption="app/serializers/rest/featured\_tag\_serializer.rb" >}}

View File

@ -0,0 +1,70 @@
---
title: Field
description: Represents a profile field as a name-value pair with optional verification.
menu:
docs:
parent: entities
---
## Example
{{< code title="Excerpt from Account\[source\]" >}}
```javascript
"fields": [
{
"name": "Website",
"value": "https://trwnh.com",
"verified_at": "2019-08-29T04:14:55.571+00:00"
},
{
"name": "Sponsor",
"value": "https://liberapay.com/at",
"verified_at": "2019-11-15T10:06:15.557+00:00"
},
{
"name": "Fan of:",
"value": "Punk-rock and post-hardcore (Circa Survive, letlive., La Dispute, THE FEVER 333)Manga (Yu-Gi-Oh!, One Piece, JoJo's Bizarre Adventure, Death Note, Shaman King)Platformers and RPGs (Banjo-Kazooie, Boktai, Final Fantasy Crystal Chronicles)",
"verified_at": null
},
{
"name": "Main topics:",
"value": "systemic analysis, design patterns, anticapitalism, info/tech freedom, theory and philosophy, and otherwise being a genuine and decent wholesome poster. i'm just here to hang out and talk to cool people!",
"verified_at": null
}
],
```
{{< /code >}}
## Required attributes
### `name`
**Description:** The key of a given field's key-value pair.
**Type:** String
**Version history:** Added in 2.4.0
### `value`
**Description:** The value associated with the `name` key.
**Type:** String \(HTML\)
**Version history:** Added in 2.4.0
## Optional attributes
### `verified_at`
**Description:** Timestamp of when the server verified a URL value for a rel="me" link.
**Type:** String \(ISO 8601 Datetime\) if `value` is a verified URL. Otherwise, null
**Version history:** Added in 2.6.0
## See also
* [Account\#fields](account.md#fields)
* [Source\#fields](source.md#fields)
{{< page-ref page="account.md" >}}
{{< page-ref page="source.md" >}}

View File

@ -0,0 +1,92 @@
---
title: Filter
description: >-
Represents a user-defined filter for determining which statuses should not be
shown to the user.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "8449",
"phrase": "test",
"context": [
"home",
"notifications",
"public",
"thread"
],
"whole_word": false,
"expires_at": "2019-11-26T09:08:06.254Z",
"irreversible": true
}
```
## Attributes
### `id`
**Description:** The ID of the filter in the database.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.4.3
### `phrase`
**Description:** The text to be filtered.
**Type:** String
**Version history:** Added in 2.4.3
### `context`
**Description:** The contexts in which the filter should be applied.
**Type:** Array of String \(Enumerable anyOf\)
- `home` = home timeline
- `notifications` = notifications timeline
- `public` = public timelines
- `thread` = expanded thread of a detailed status
**Version history:** Added in 2.4.3
### `expires_at`
**Description:** When the filter should no longer be applied
**Type:** String \(ISO 8601 Datetime\), or null if the filter does not expire
**Version history:** Added in 2.4.3
### `irreversible`
**Description:** Should matching entities in home and notifications be dropped by the server?
**Type:** Boolean
**Version history:** Added in 2.4.3
### `whole_word`
**Description:** Should the filter consider word boundaries?
**Type:** Boolean
**Version history:** Added in 2.4.3
## Implementation notes
If `whole_word` is true , client app should do:
* Define word constituent character for your app. In the official implementation, its `[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.
Please check `app/javascript/mastodon/selectors/index.js` and `app/lib/feed_manager.rb` in the Mastodon source code for more details.
## See also
{{< page-ref page="methods/accounts/filters.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/lib/feed_manager.rb" caption="app/lib/feed\_manager.rb" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/javascript/mastodon/selectors/index.js" caption="app/javascript/mastodon/selectors/index.js" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/filter_serializer.rb" caption="app/serializers/rest/filter\_serializer.rb" >}}

View File

@ -0,0 +1,48 @@
---
title: History
description: Represents daily usage history of a hashtag.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"day": "1574553600",
"uses": "200",
"accounts": "31"
}
```
## Required attributes
### `day`
**Description:** UNIX timestamp on midnight of the given day.
**Type:** String \(UNIX timestamp\)
**Version history:** Added in 2.4.1
### `uses`
**Description:** the counted usage of the tag within that day.
**Type:** String \(cast from an integer\)
**Version history:** Added in 2.4.1
### `accounts`
**Description:** the total of accounts using the tag within that day.
**Type:** String \(cast from an integer\)
**Version history:** Added in 2.4.1
## See also
* [Tag\#history](tag.md#history)
{{< page-ref page="tag.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/17159625b3e2c6d94509c0c2879ca80efbac6846/app/models/tag.rb\#L101-L115" caption="app/models/tag.rb L101-115 -- history days\[\]" >}}

View File

@ -0,0 +1,61 @@
---
title: IdentityProof
menu:
docs:
parent: entities
---
```javascript
{
"provider": "Keybase",
"provider_username": "gargron",
"updated_at": "2019-07-21T20:14:39.596Z",
"proof_url": "https://keybase.io/gargron/sigchain#5cfc20c7018f2beefb42a68836da59a792e55daa4d118498c9b1898de7e845690f",
"profile_url": "https://keybase.io/gargron"
}
```
## Attributes
### `provider`
**Description:** The name of the identity provider.
**Type:** String
**Version history:** Added in 2.8.0
### `provider_username`
**Description:** The account owner's username on the identity provider's service.
**Type:** String
**Version history:** Added in 2.8.0
### `profile_url`
**Description:** The account owner's profile URL on the identity provider.
**Type:** String \(URL\)
**Version history:** Added in 2.8.0
### `proof_url`
**Description:** A link to a statement of identity proof, hosted by the identity provider.
**Type:** String \(URL\)
**Version history:** Added in 2.8.0
### `updated_at`
**Description:** The name of the identity provider.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 2.8.0
## See also
* [GET /api/v1/accounts/:id/identity\_proofs](../methods/accounts/#identity-proofs)
* /api/proofs
* [About identity proofs](../user/contacts.md#identity-proofs)
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/identity_proof_serializer.rb" caption="app/serializers/rest/identity\_proof\_serializer.rb" >}}

View File

@ -0,0 +1,173 @@
---
title: Instance
description: Represents the software instance of Mastodon running on this domain.
menu:
docs:
parent: entities
---
## Example
```javascript
"uri": "mastodon.social",
"title": "Mastodon",
"short_description": "Server run by the main developers of the project <img draggable=\"false\" alt=\"🐘\" class=\"emojione\" src=\"https://mastodon.social/emoji/1f418.svg\" /> It is not focused on any particular niche interest - everyone is welcome as long as you follow our code of conduct!",
"description": "Server run by the main developers of the project <img draggable=\"false\" alt=\"🐘\" class=\"emojione\" src=\"https://mastodon.social/emoji/1f418.svg\" /> It is not focused on any particular niche interest - everyone is welcome as long as you follow our code of conduct!",
"email": "staff@mastodon.social",
"version": "3.0.1",
"urls": {
"streaming_api": "wss://mastodon.social"
},
"stats": {
"user_count": 415526,
"status_count": 17085754,
"domain_count": 11834
},
"thumbnail": "https://files.mastodon.social/site_uploads/files/000/000/001/original/vlcsnap-2018-08-27-16h43m11s127.png",
"languages": [
"en"
],
"registrations": true,
"approval_required": false,
"contact_account": {
"id": "1",
"username": "Gargron",
"acct": "Gargron",
"display_name": "Eugen",
"locked": false,
"bot": false,
"created_at": "2016-03-16T14:34:26.392Z",
"note": "<p>Developer of Mastodon and administrator of mastodon.social. I post service announcements, development updates, and personal stuff.</p>",
"url": "https://mastodon.social/@Gargron",
"avatar": "https://files.mastodon.social/accounts/avatars/000/000/001/original/d96d39a0abb45b92.jpg",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/000/001/original/d96d39a0abb45b92.jpg",
"header": "https://files.mastodon.social/accounts/headers/000/000/001/original/c91b871f294ea63e.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/000/001/original/c91b871f294ea63e.png",
"followers_count": 317112,
"following_count": 453,
"statuses_count": 60903,
"last_status_at": "2019-11-26T21:14:44.522Z",
"emojis": [],
"fields": [
{
"name": "Patreon",
"value": "<a href=\"https://www.patreon.com/mastodon\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://www.</span><span class=\"\">patreon.com/mastodon</span><span class=\"invisible\"></span></a>",
"verified_at": null
},
{
"name": "Homepage",
"value": "<a href=\"https://zeonfederated.com\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">zeonfederated.com</span><span class=\"invisible\"></span></a>",
"verified_at": "2019-07-15T18:29:57.191+00:00"
}
]
}
}
```
## Required attributes
### `uri`
**Description:** The domain name of the instance.
**Type:** String
**Version history:** Added in 1.1.0
### `title`
**Description:** The title of the website.
**Type:** String
**Version history:** Added in 1.1.0
### `description`
**Description:** Admin-defined description of the Mastodon site.
**Type:** String
**Version history:** Added in 1.1.0
### `short_description`
**Description:** A shorter description defined by the admin.
**Type:** String
**Version history:** Added in 2.9.2
### `email`
**Description:** An email that may be contacted for any inquiries.
**Type:** String
**Version history:** Added in 1.1.0
### `version`
**Description:** The version of Mastodon installed on the instance.
**Type:** String
**Version history:** Added in 1.3.0
### `languages`
**Description:** Primary langauges of the website and its staff.
**Type:** Array of String \(ISO 639 Part 1-5 language codes\)
**Version history:** Added in 2.3.0
### `registrations`
**Description:** Whether registrations are enabled.
**Type:** Boolean
**Version history:** Added in 2.7.2
### `approval_required`
**Description:** Whether registrations require moderator approval.
**Type:** Boolean
**Version history:** Added in 2.9.2
### `urls`
**Description:** URLs of interest for clients apps.
**Type:** Hash \(`streaming_api`\)
**Version history:** Added in 1.4.2
#### `urls[streaming_api]`
Websockets address for push streaming. String \(URL\).
### `stats`
**Description:** Statistics about how much information the instance contains.
**Type:** Hash \(`user_count`, `status_count`, `domain_count`\)
**Version history:** Added in 1.6.0
#### `user_count`
Users registered on this instance. Number.
#### `status_count`
Statuses authored by users on instance. Number.
#### `domain_count`
Domains federated with this instance. Number.
## Optional attributes
### `thumbnail`
**Description:** Banner image for the website.
**Type:** String \(URL\) or null
**Version history:** Added in 1.6.1
### `contact_account`
**Description:** A user that can be contacted, as an alternative to `email`.
**Type:** [Account](account.md) or null
**Version history:** Added in 2.3.0
## See also
{{< page-ref page="methods/instance.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/instance_serializer.rb" caption="app/serializers/rest/instance\_serializer.rb" >}}

View File

@ -0,0 +1,39 @@
---
title: List
description: Represents a list of some users that the authenticated user follows.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "12249",
"title": "Friends"
}
```
## Required attributes
### `id`
**Description:** The internal database ID of the list.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.1.0
### `title`
**Description:** The user-defined title of the list.
**Type:** String
**Version history:** Added in 2.1.0
## See also
{{< page-ref page="methods/timelines/lists.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/list_serializer.rb" caption="app/serializers/rest/list\_serializer.rb" >}}

View File

@ -0,0 +1,66 @@
---
title: Marker
menu:
docs:
parent: entities
---
## Example
```javascript
{
"home": {
"last_read_id": "103194548672408537",
"version": 462,
"updated_at": "2019-11-24T19:39:39.337Z"
},
"notifications": {
"last_read_id": "35050107",
"version": 356,
"updated_at": "2019-11-25T13:47:31.333Z"
}
}
```
## Base attributes
### `home`
**Description:** Information about the user's position in the home timeline.
**Type:** Hash
**Version history:** Added in 3.0.0
### `notifications`
**Description:** Information about the user's position in their notifications.
**Type:** Hash
**Version history:** Added in 3.0.0
## Nested attributes
### `last_read_id`
**Description:** The ID of the most recently viewed entity.
**Type:** String \(cast from integer but not guaranteed to be a number\)
**Version history:** Added in 3.0.0
### `updated_at`
**Description:** The timestamp of when the marker was set.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 3.0.0
### `version`
**Description:** Used for locking to prevent write conflicts.
**Type:** Number
**Version history:** Added in 3.0.0
## See also
{{< page-ref page="methods/timelines/markers.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/marker_serializer.rb" caption="app/serializers/rest/marker\_serializer.rb" >}}

View File

@ -0,0 +1,66 @@
---
title: Mention
description: Represents a mention of a user within the content of a status.
menu:
docs:
parent: entities
---
## Example
{{< code title="excerpt from GET status:" >}}
```javascript
{
"mentions": [
{
"id": "952529",
"username": "alayna",
"url": "https://desvox.es/users/alayna",
"acct": "alayna@desvox.es"
},
{
"id": "14715",
"username": "trwnh",
"url": "https://mastodon.social/@trwnh",
"acct": "trwnh"
}
]
}
```
{{< /code >}}
## Required attributes
### `id`
**Description:** The account id of the mentioned user.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 0.6.0
### `username`
**Description:** The username of the mentioned user.
**Type:** String
**Version history:** Added in 0.6.0
### `acct`
**Description:** The webfinger acct: URI of the mentioned user. Equivalent to `username` for local users, or `username@domain` for remote users.
**Type:** String
**Version history:** Added in 0.6.0
### `url`
**Description:** The location of the mentioned user's profile.
**Type:** String \(URL\)
**Version history:** Added in 0.6.0
## See also
* [GET /api/v1/statuses/:id](../methods/statuses/#view-specific-status)
* [Status\#mentions](status.md#mentions)
{{< page-ref page="status.md" >}}

View File

@ -0,0 +1,111 @@
---
title: Notification
description: Represents a notification of an event relevant to the user.
menu:
docs:
parent: entities
---
## Example
{{< code title="excerpt from GET notifications" >}}
```javascript
[
{
"id": "34975861",
"type": "mention",
"created_at": "2019-11-23T07:49:02.064Z",
"account": {
"id": "971724",
"username": "zsc",
"acct": "zsc",
...
},
"status": {
"id": "103186126728896492",
"created_at": "2019-11-23T07:49:01.940Z",
"in_reply_to_id": "103186038209478945",
"in_reply_to_account_id": "14715",
...
}
},
{
"id": "34975535",
"type": "favourite",
"created_at": "2019-11-23T07:29:18.903Z",
"account": {
"id": "297420",
"username": "haskal",
"acct": "haskal@cybre.space",
...
},
"status": {
"id": "103186046267791694",
"created_at": "2019-11-23T07:28:34.210Z",
...
},
"account": {
"id": "14715",
"username": "trwnh",
"acct": "trwnh",
...
},
...
}
},
...
]
```
{{< /code >}}
## Required attributes
### `id`
**Description:** The id of the notification in the database.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 0.9.9
### `type`
**Description:** The type of event that resulted in the notification.
**Type:** String \(Enumerable oneOf\)
- `follow` = Someone followed you
- `mention` = Someone mentioned you in their status
- `reblog` = Someone boosted one of your statuses
- `favourite` = Someone favourited one of your statuses
- `poll` = A poll you have voted in or created has ended
**Version history:** Added in 0.9.9. `poll` added in 2.8.0.
### `created_at`
**Description:** The timestamp of the notification.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 0.9.9
### `account`
**Description:** The account that performed the action that generated the notification.
**Type:** [Account](account.md)
**Version history:** Added in 0.9.9
## Optional attributes
### `status`
**Description:** Status that was the object of the notification, e.g. in mentions, reblogs, favourites, or polls.
**Type:** [Status](status.md)
**Version history:** Added in 0.9.9
## See also
{{< page-ref page="account.md" >}}
{{< page-ref page="status.md" >}}
{{< page-ref page="methods/notifications.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/notification_serializer.rb" caption="app/serializers/rest/notification\_serializer.rb" >}}

119
content/en/entities/poll.md Normal file
View File

@ -0,0 +1,119 @@
---
title: Poll
description: Represents a poll attached to a status.
menu:
docs:
parent: entities
---
```javascript
{
"id": "34830",
"expires_at": "2019-12-05T04:05:08.302Z",
"expired": true,
"multiple": false,
"votes_count": 10,
"voters_count": null,
"voted": true,
"own_votes": [
1
],
"options": [
{
"title": "accept",
"votes_count": 6
},
{
"title": "deny",
"votes_count": 4
}
],
"emojis": []
}
```
## Poll attributes <a id="poll"></a>
### `id`
**Description:** The ID of the poll in the database.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.8.0
### `expires_at`
**Description:** When the poll ends.
**Type:** String \(ISO 8601 Datetime\), or null if the poll does not end
**Version history:** Added in 2.8.0
### `expired`
**Description:** Is the poll currently expired?
**Type:** Boolean
**Version history:** Added in 2.8.0
### `multiple`
**Description:** Does the poll allow multiple-choice answers?
**Type:** Boolean
**Version history:** Added in 2.8.0
### `votes_count`
**Description:** How many votes have been received.
**Type:** Number
**Version history:** Added in 2.8.0
### `voters_count`
**Description:** How many unique accounts have voted on a multiple-choice poll.
**Type:** Number, or null if `multiple` is false.
**Version history:** Added in 2.8.0
### `voted`
**Description:** When called with a user token, has the authorized user voted?
**Type:** Boolean, or null if no current user
**Version history:** Added in 2.8.0
### `own_votes`
**Description:** When called with a user token, which options has the authorized user chosen? Contains an array of index values for `options`.
**Type:** Array of Number, or null if no current user
**Version history:** Added in 2.8.0
### `options[]`
**Description:** Possible answers for the poll.
**Type:** Array of Hash
**Version history:** Added in 2.8.0
#### `options[][title]`
The text value of the poll option. String.
#### `options[][votes_count]`
The number of received votes for this option. Number, or null if results are not published yet.
### `emojis`
**Description:** Custom emoji to be used for rendering poll options.
**Type:** Array of Emoji
**Version history:** Added in 2.8.0
## See also
* [Status\#poll](status.md#poll)
* [/api/v1/polls]({{< relref "../methods/statuses/polls.md" >}})
{{< page-ref page="status.md" >}}
{{< page-ref page="methods/statuses/polls.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/poll_serializer.rb" caption="app/serializers/rest/poll\_serializer.rb" >}}

View File

@ -0,0 +1,71 @@
---
title: Preferences
description: Represents a user's preferences.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"posting:default:visibility": "public",
"posting:default:sensitive": false,
"posting:default:language": null,
"reading:expand:media": "default",
"reading:expand:spoilers": false
}
```
## Attributes
### `posting:default:visibility`
**Description:** Default visibility for new posts. Equivalent to [Source\#privacy](source.md#privacy).
**Type:** String \(Enumerable, oneOf\)
- `public` = Public post
- `unlisted` = Unlisted post
- `private` = Followers-only post
- `direct` = Direct post
**Version history:** Added in 2.8.0
### `posting:default:sensitive`
**Description:** Default sensitivity flag for new posts. Equivalent to [Source\#sensitive](source.md#sensitive).
**Type:** Boolean
**Version history:** Added in 2.8.0
### `posting:default:language`
**Description:** Default language for new posts. Equivalent to [Source\#language](source.md#language)
**Type:** String \(ISO 639-1 language two-letter code\), or null
**Version history:** Added in 2.8.0
### `reading:expand:media`
**Description:** Whether media attachments should be automatically displayed or blurred/hidden.
**Type:** String \(Enumerable, oneOf\)
- `default` = Hide media marked as sensitive
- `show_all` = Always show all media by default, regardless of sensitivity
- `hide_all` = Always hide all media by default, regardless of sensitivity
**Version history:** Added in 2.8.0
### `reading:expand:spoilers`
**Description:** Whether CWs should be expanded by default.
**Type:** Boolean
**Version history:** Added in 2.8.0
## See also
* [GET /api/v1/accounts/verify\_credentials](../methods/accounts/#verify-account-credentials)
* [PATCH /api/v1/accounts/update\_credentials](../methods/accounts/#update-account-credentials)
* [GET /api/v1/preferences](../methods/accounts/preferences.md#view-user-preferences)
{{< page-ref page="methods/accounts/preferences.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/preferences_serializer.rb" caption="app/serializers/rest/preferences\_serializer.rb" >}}

View File

@ -0,0 +1,79 @@
---
title: PushSubscription
description: Represents a subscription to the push streaming server.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": 328183,
"endpoint": "https://yourdomain.example/listener",
"alerts": {
"follow": false,
"favourite": false,
"reblog": false,
"mention": true,
"poll": false
},
"server_key": "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M="
}
```
## Required attributes <a id="push-subscription"></a>
### `id`
**Description:** The id of the push subscription in the database.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 2.4.0
### `endpoint`
**Description:** Where push alerts will be sent to.
**Type:** String \(URL\)
**Version history:** Added in 2.4.0
### `server_key`
**Description:** The streaming server's VAPID key.
**Type:** String
**Version history:** Added in 2.4.0
### `alerts`
**Description:** Which alerts should be delivered to the `endpoint`.
**Type:** Hash
**Version history:** Added in 2.4.0. `alerts[poll]` added in 2.8.0.
#### `alerts[follow]`
Receive a push notification when someone has followed you? Boolean.
#### `alerts[favourite]`
Receive a push notification when a status you created has been favourited by someone else? Boolean.
#### `alerts[mention]`
Receive a push notification when someone else has mentioned you in a status? Boolean.
#### `alerts[reblog]`
Receive a push notification when a status you created has been boosted by someone else? Boolean.
#### `alerts[poll]`
Receive a push notification when a poll you voted in or created has ended? Boolean. Added in 2.8.0
## See also
{{< page-ref page="methods/notifications/push.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/web_push_subscription_serializer.rb" caption="app/serializers/rest/web\_push\_subscription\_serializer.rb" >}}

View File

@ -0,0 +1,104 @@
---
title: Relationship
description: >-
Represents the relationship between accounts, such as following / blocking /
muting / etc.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "1",
"following": true,
"showing_reblogs": true,
"followed_by": true,
"blocking": false,
"blocked_by": false,
"muting": false,
"muting_notifications": false,
"requested": false,
"domain_blocking": false,
"endorsed": false
}
```
## Required attributes <a id="relationship"></a>
### `id`
**Description:** The account id.
**Type:** String \(cast from an integer, but not guaranteed to be a number\)
**Version history:** Added in 0.6.0
### `following`
**Description:** Are you following this user?
**Type:** Boolean
**Version history:** Added in 0.6.0
### `requested`
**Description:** Do you have a pending follow request for this user?
**Type:** Boolean
**Version history:** Added in 0.9.9
### `endorsed`
**Description:** Are you featuring this user on your profile?
**Type:** Boolean
**Version history:** Added in 2.5.0
### `followed_by`
**Description:** Are you followed by this user?
**Type:** Boolean
**Version history:** Added in 0.6.0
### `muting`
**Description:** Are you muting this user?
**Type:** Boolean
**Version history:** Added in 1.1.0
### `muting_notifications`
**Description:** Are you muting notifications from this user?
**Type:** Boolean
**Version history:** Added in 2.1.0
### `showing_reblogs`
**Description:** Are you receiving this user's boosts in your home timeline?
**Type:** Boolean
**Version history:** Added in 2.1.0
### `blocking`
**Description:** Are you blocking this user?
**Type:** Boolean
**Version history:** Added in 0.6.0
### `domain_blocking`
**Description:** Are you blocking this user's domain?
**Type:** Boolean
**Version history:** Added in 1.4.0
### `blocked_by`
**Description:** Is this user blocking you?
**Type:** Boolean
**Version history:** Added in 2.8.0
## See also
* [GET /api/v1/accounts/relationships](../methods/accounts/#check-relationships-to-other-accounts)
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/relationship_serializer.rb" caption="app/serializers/rest/relationship\_serializer.rb" >}}

View File

@ -0,0 +1,30 @@
---
title: Report
description: >-
Reports filed against users and/or statuses, to be taken action on by
moderators.
menu:
docs:
parent: entities
---
{{< hint style="danger" >}}
This page is currently under construction.
{{< /hint >}}
## Example
```text
```
## Attributes
## See also
{{< page-ref page="methods/accounts/reports.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/report_serializer.rb" caption="app/serializers/rest/report\_serializer.rb" >}}

View File

@ -0,0 +1,113 @@
---
title: Results
description: Represents the results of a search.
menu:
docs:
parent: entities
---
## Example
{{< code title="Truncated sample search for q=cats limit=2" >}}
```javascript
{
"accounts": [
{
"id": "180744",
"username": "catstar",
"acct": "catstar@catgram.jp",
"display_name": "catstar",
...
},
{
"id": "214293",
"username": "catsareweird",
"acct": "catsareweird",
"display_name": "Cats Are Weird",
...
}
],
"statuses": [
{
"id": "103085519055545958",
"created_at": "2019-11-05T13:23:09.000Z",
...
"content": "<p>cats<br>cats never change</p>",
...
},
{
"id": "101068121469614510",
"created_at": "2018-11-14T06:31:48.000Z",
...
"spoiler_text": "Cats",
...
"content": "<p>Cats are inherently good at self-care. </p><p><a href=\"https://mspsocial.net/tags/cats\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>cats</span></a></p>",
...
],
"hashtags": [
{
"name": "cats",
"url": "https://mastodon.social/tags/cats",
"history": [
{
"day": "1574553600",
"uses": "10",
"accounts": "9"
},
...
]
},
{
"name": "catsofmastodon",
"url": "https://mastodon.social/tags/catsofmastodon",
"history": [
{
"day": "1574553600",
"uses": "6",
"accounts": "5"
},
...
]
}
]
}
```
{{< /code >}}
## Required attributes
### `accounts`
**Description:** Accounts which match the given query
**Type:** Array of [Account](account.md)
**Version history:** Added in x.x.x
### `statuses`
**Description:** Statuses which match the given query
**Type:** Array of [Status](status.md)
**Version history:** Added in x.x.x
### `hashtags`
**Description:** Hashtags which match the given query
**Type:** Array of [Tag](tag.md) \(v2\). Array of String \(v1\).
**Version history:** v1 added in 1.1.0 and deprecated in 3.0.0. v2 added in 2.4.1 and replaced v1 in 3.0.0.
## See also
{{< page-ref page="methods/search.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/search_serializer.rb" caption="app/serializers/rest/search\_serializer.rb" >}}

View File

@ -0,0 +1,98 @@
---
title: ScheduledStatus
description: Represents a status that will be published at a future scheduled date.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "3221",
"scheduled_at": "2019-12-05T12:33:01.000Z",
"params": {
"text": "test content",
"media_ids": null,
"sensitive": null,
"spoiler_text": null,
"visibility": null,
"scheduled_at": null,
"poll": null,
"idempotency": null,
"in_reply_to_id": null,
"application_id": 596551
},
"media_attachments": []
}
```
## Required attributes
### id
**Description:** ID of the scheduled status in the database.
**Type:** String \(cast from an integer but not guaranteed to be a number\)
**Version history:** Added in 2.7.0
### scheduled\_at
**Description:** ID of the status in the database.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 2.7.0
### params
#### params\[text\]
#### params\[visibility\]
#### params\[application\_id\]
### media\_attachments
## Optional attributes
### params
#### in\_reply\_to\_id
#### media\_ids
#### sensitive
#### spoiler\_text
#### scheduled\_at
## ScheduledStatus <a id="scheduledstatus"></a>
| Attribute | Type | Nullable | Added in |
| :--- | :--- | :--- | :--- |
| `id` | String | No | 2.7.0 |
| `scheduled_at` | String \(Datetime\) | No | 2.7.0 |
| `params` | [StatusParams]() | No | 2.7.0 |
| `media_attachments` | Array of [Attachment]() | No | 2.7.0 |
### StatusParams <a id="statusparams"></a>
| Attribute | Type | Nullable | Added in |
| :--- | :--- | :--- | :--- |
| `text` | String | No | 2.7.0 |
| `in_reply_to_id` | String | | 2.7.0 |
| `media_ids` | Array of String | | 2.7.0 |
| `sensitive` | Boolean | | 2.7.0 |
| `spoiler_text` | String | | 2.7.0 |
| `visibility` | [String \(Enum\)]() | No | 2.7.0 |
| `scheduled_at` | String \(Datetime\) | | 2.7.0 |
| `application_id` | String | No | 2.7.0 |
## See also
{{< page-ref page="methods/statuses/scheduled_statuses.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/scheduled_status_serializer.rb" caption="app/serializers/rest/scheduled\_status\_serializer.rb" >}}

View File

@ -0,0 +1,101 @@
---
title: Source
description: >-
Represents display or publishing preferences of user's own account. Returned
as an additional entity when verifying and updated credentials, as an
attribute of Account.
menu:
docs:
parent: entities
---
## Example
{{< code title="Excerpt from GET accounts/verify\_credentials" >}}
```javascript
"source": {
"privacy": "public",
"sensitive": false,
"language": "",
"note": "i have approximate knowledge of many things. perpetual student. (nb/ace/they)\r\n\r\nxmpp/email: a@trwnh.com\r\nhttps://trwnh.com\r\nhelp me live: https://liberapay.com/at or https://paypal.me/trwnh\r\n\r\n- my triggers are moths and glitter\r\n- i have all notifs except mentions turned off, so please interact if you wanna be friends! i literally will not notice otherwise\r\n- dm me if i did something wrong, so i can improve\r\n- purest person on fedi, do not lewd in my presence\r\n- #1 ami cole fan account\r\n\r\n:fatyoshi:",
"fields": [
{
"name": "Website",
"value": "https://trwnh.com",
"verified_at": "2019-08-29T04:14:55.571+00:00"
},
{
"name": "Sponsor",
"value": "https://liberapay.com/at",
"verified_at": "2019-11-15T10:06:15.557+00:00"
},
{
"name": "Fan of:",
"value": "Punk-rock and post-hardcore (Circa Survive, letlive., La Dispute, THE FEVER 333)Manga (Yu-Gi-Oh!, One Piece, JoJo's Bizarre Adventure, Death Note, Shaman King)Platformers and RPGs (Banjo-Kazooie, Boktai, Final Fantasy Crystal Chronicles)",
"verified_at": null
},
{
"name": "Main topics:",
"value": "systemic analysis, design patterns, anticapitalism, info/tech freedom, theory and philosophy, and otherwise being a genuine and decent wholesome poster. i'm just here to hang out and talk to cool people!",
"verified_at": null
}
],
"follow_requests_count": 0
}
```
{{< /code >}}
## Base attributes
### `note`
**Description:** Profile bio.
**Type:** String
**Version history:** Added in 1.5.0
### `fields`
**Description:** Metadata about the account.
**Type:** Array of [Field]({{< relref "field.md" >}})
**Version history:** Added in 2.4.0
## Nullable attributes
### `privacy`
**Description:** The default post privacy to be used for new statuses.
**Type:** String \(Enumerable, oneOf\)
- `public` = Public post
- `unlisted` = Unlisted post
- `private` = Followers-only post
- `direct` = Direct post
**Version history:** Added in 1.5.0
### `sensitive`
**Description:** Whether new statuses should be marked sensitive by default.
**Type:** Boolean
**Version history:** Added in 1.5.0
### `language`
**Description:** The default posting language for new statuses.
**Type:** String \(ISO 639-1 language two-letter code\)
**Version history:** Added in 2.4.2
### `follow_requests_count`
**Description:** The number of pending follow requests.
**Type:** Number
**Version history:** Added in 3.0.0
## See also
* [Account\#source](account.md#source)
* [POST /api/v1/accounts/verify\_credentials](../methods/accounts/#verify-account-credentials)
* [PATCH /api/v1/accounts/update\_credentials](../methods/accounts/#update-account-credentials)
{{< page-ref page="account.md" >}}

View File

@ -0,0 +1,291 @@
---
title: Status
description: Represents a status posted by an account.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"id": "103270115826048975",
"created_at": "2019-12-08T03:48:33.901Z",
"in_reply_to_id": null,
"in_reply_to_account_id": null,
"sensitive": false,
"spoiler_text": "",
"visibility": "public",
"language": "en",
"uri": "https://mastodon.social/users/Gargron/statuses/103270115826048975",
"url": "https://mastodon.social/@Gargron/103270115826048975",
"replies_count": 5,
"reblogs_count": 6,
"favourites_count": 11,
"favourited": false,
"reblogged": false,
"muted": false,
"bookmarked": false,
"content": "<p>&quot;I lost my inheritance with one wrong digit on my sort code&quot;</p><p><a href=\"https://www.theguardian.com/money/2019/dec/07/i-lost-my-193000-inheritance-with-one-wrong-digit-on-my-sort-code\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://www.</span><span class=\"ellipsis\">theguardian.com/money/2019/dec</span><span class=\"invisible\">/07/i-lost-my-193000-inheritance-with-one-wrong-digit-on-my-sort-code</span></a></p>",
"reblog": null,
"application": {
"name": "Web",
"website": null
},
"account": {
"id": "1",
"username": "Gargron",
"acct": "Gargron",
"display_name": "Eugen",
"locked": false,
"bot": false,
"discoverable": true,
"group": false,
"created_at": "2016-03-16T14:34:26.392Z",
"note": "<p>Developer of Mastodon and administrator of mastodon.social. I post service announcements, development updates, and personal stuff.</p>",
"url": "https://mastodon.social/@Gargron",
"avatar": "https://files.mastodon.social/accounts/avatars/000/000/001/original/d96d39a0abb45b92.jpg",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/000/001/original/d96d39a0abb45b92.jpg",
"header": "https://files.mastodon.social/accounts/headers/000/000/001/original/c91b871f294ea63e.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/000/001/original/c91b871f294ea63e.png",
"followers_count": 322930,
"following_count": 459,
"statuses_count": 61323,
"last_status_at": "2019-12-10T08:14:44.811Z",
"emojis": [],
"fields": [
{
"name": "Patreon",
"value": "<a href=\"https://www.patreon.com/mastodon\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://www.</span><span class=\"\">patreon.com/mastodon</span><span class=\"invisible\"></span></a>",
"verified_at": null
},
{
"name": "Homepage",
"value": "<a href=\"https://zeonfederated.com\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">zeonfederated.com</span><span class=\"invisible\"></span></a>",
"verified_at": "2019-07-15T18:29:57.191+00:00"
}
]
},
"media_attachments": [],
"mentions": [],
"tags": [],
"emojis": [],
"card": {
"url": "https://www.theguardian.com/money/2019/dec/07/i-lost-my-193000-inheritance-with-one-wrong-digit-on-my-sort-code",
"title": "I lost my £193,000 inheritance with one wrong digit on my sort code",
"description": "When Peter Teichs money went to another Barclays customer, the bank offered £25 as a token gesture",
"type": "link",
"author_name": "",
"author_url": "",
"provider_name": "",
"provider_url": "",
"html": "",
"width": 0,
"height": 0,
"image": null,
"embed_url": ""
},
"poll": null
}
```
## Base attributes
### `id`
**Description:** ID of the status in the database.
**Type:** String \(cast from an integer but not guaranteed to be a number\)
**Version history:** Added in 0.1.0
### `uri`
**Description:** URI of the status used for federation.
**Type:** String
**Version history:** Added in 0.1.0
### `created_at`
**Description:** HTML-encoded status content.
**Type:** String \(ISO 8601 Datetime\)
**Version history:** Added in 0.1.0
### `account`
**Description:** The account that authored this status.
**Type:** [Account](account.md)
**Version history:** Added in 0.1.0
### `content`
**Description:** HTML-encoded status content.
**Type:** String \(HTML\)
**Version history:** Added in 0.1.0
### `text`
**Description:** Plain-text source of a status. Returned instead of `content` when status is deleted, so the user may redraft from the source text without the client having to reverse-engineer the original text from the HTML content.
**Type:** String
**Version history:** Added in 2.9.0
### `visibility`
**Description:** HTML-encoded status content.
**Type:** String \(Enumerable oneOf\)
- `public` = Visible to everyone, shown in public timelines.
- `unlisted` = Visible to public, but not included in public timelines.
- `private` = Visible to followers only, and to any mentioned users.
- `direct` = Visible only to mentioned users.
**Version history:** Added in 0.9.9
### `sensitive`
**Description:** Is this status marked as sensitive content?
**Type:** Boolean
**Version history:** Added in 0.9.9
### `spoiler_text`
**Description:** Subject or summary line, below which status content is collapsed until expanded.
**Type:** String
**Version history:** Added in 1.0.0
### `media_attachments`
**Description:** Media that is attached to this status.
**Type:** Array of [Attachment](attachment.md)
**Version history:** Added in 0.6.0
### `application`
**Description:** The application used to post this status.
**Type:** [Application](application.md)
**Version history:** Added in 0.9.9
## Rendering attributes
### `mentions`
**Description:** Mentions of users within the status content.
**Type:** Array of [Mention]({{< relref "mention.md" >}})
**Version history:** Added in 0.6.0
### `tags`
**Description:** Hashtags used within the status content.
**Type:** Array of [Tag](tag.md)
**Version history:** Added in 0.9.0
### `emojis`
**Description:** Custom emoji to be used when rendering status content.
**Type:** Array of [Emoji](emoji.md)
**Version history:** Added in 2.0.0
## Informational attributes
### `reblogs_count`
**Description:** How many boosts this status has received.
**Type:** Number
**Version history:** Added in 0.1.0
### `favourites_count`
**Description:** How many favourites this status has received.
**Type:** Number
**Version history:** Added in 0.1.0
### `replies_count`
**Description:** How many replies this status has received.
**Type:** Number
**Version history:** Added in 2.5.0
## Nullable attributes
### `url`
**Description:** A link to the status's HTML representation.
**Type:** String \(URL\)
**Version history:** Added in 0.1.0
### `in_reply_to_id`
**Description:** ID of the status being replied.
**Type:** String \(cast from an integer but not guaranteed to be a number\)
**Version history:** Added in 0.1.0
### `in_reply_to_account_id`
**Description:** ID of the account being replied to.
**Type:** String \(cast from an integer but not guaranteed to be a number\)
**Version history:** Added in 1.0.0
### `reblog`
**Description:** ID of the status in the database.
**Type:** [Status](status.md)
**Version history:** Added in 0.1.0
### `poll`
**Description:** The poll attached to the status.
**Type:** [Poll]({{< relref "poll.md" >}})
**Version history:** Added in 2.8.0
### `card`
**Description:** Preview card for links included within status content.
**Type:** [Card]({{< relref "card.md" >}})
**Version history:** Added in 2.6.0
### `language`
**Description:** A link to the status's HTML representation.
**Type:** String \(ISO 639 Part 1 two-letter language code\)
**Version history:** Added in 1.4.0
## Authorized user attributes
### `favourited`
**Description:** Have you favourited this status?
**Type:** Boolean
**Version history:** Added in 0.1.0
### `reblogged`
**Description:** Have you boosted this status?
**Type:** Boolean
**Version history:** Added in 0.1.0
### `muted`
**Description:** Have you muted notifications for this status's conversation?
**Type:** Boolean
**Version history:** Added in 1.4.0
### `bookmarked`
**Description:** Have you bookmarked this status?
**Type:** Boolean
**Version history:** Added in 3.1.0
### `pinned`
**Description:** Have you pinned this status? Only appears if the status is pinnable.
**Type:** Boolean
**Version history:** Added in 1.6.0
## See also
* GET accounts/:id/statuses
* GET /api/v2/search
{{< page-ref page="methods/statuses.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/status_serializer.rb" caption="app/serializers/rest/status\_serializer.rb" >}}

View File

@ -0,0 +1,89 @@
---
title: Tag
description: Represents a hashtag used within the content of a status.
menu:
docs:
parent: entities
---
## Example
```javascript
{
"name": "nowplaying",
"url": "https://mastodon.social/tags/nowplaying",
"history": [
{
"day": "1574553600",
"uses": "200",
"accounts": "31"
},
{
"day": "1574467200",
"uses": "272",
"accounts": "39"
},
{
"day": "1574380800",
"uses": "345",
"accounts": "40"
},
{
"day": "1574294400",
"uses": "366",
"accounts": "46"
},
{
"day": "1574208000",
"uses": "226",
"accounts": "32"
},
{
"day": "1574121600",
"uses": "217",
"accounts": "42"
},
{
"day": "1574035200",
"uses": "214",
"accounts": "34"
}
]
}
```
## Base attributes
### `name`
**Description:** The value of the hashtag after the \# sign.
**Type:** String
**Version history:** Added in 0.9.0
### `url`
**Description:** A link to the hashtag on the instance.
**Type:** String \(URL\)
**Version history:** Added in 0.9.0
## Optional attributes
### `history`
**Description:** Usage statistics for given days.
**Type:** Array of [History]({{< relref "history.md" >}})
**Version history:** Added in 2.4.1
## See also
* [Status\#tags](status.md#tags)
* [GET /api/v1/featured\_tags/suggestions](../methods/accounts/featured_tags.md#suggested-tags-to-feature)
{{< page-ref page="status.md" >}}
{{< page-ref page="methods/search.md" >}}
{{< caption-link url="https://github.com/tootsuite/mastodon/blob/master/app/serializers/rest/tag_serializer.rb" caption="app/serializers/rest/tag\_serializer.rb" >}}

Some files were not shown because too many files have changed in this diff Show More