aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/administration/CLI_tasks/config.md157
-rw-r--r--docs/administration/CLI_tasks/database.md161
-rw-r--r--docs/administration/CLI_tasks/digest.md33
-rw-r--r--docs/administration/CLI_tasks/email.md45
-rw-r--r--docs/administration/CLI_tasks/emoji.md61
-rw-r--r--docs/administration/CLI_tasks/frontend.md96
-rw-r--r--docs/administration/CLI_tasks/general_cli_task_info.include5
-rw-r--r--docs/administration/CLI_tasks/instance.md45
-rw-r--r--docs/administration/CLI_tasks/oauth_app.md20
-rw-r--r--docs/administration/CLI_tasks/relay.md45
-rw-r--r--docs/administration/CLI_tasks/robots_txt.md21
-rw-r--r--docs/administration/CLI_tasks/uploads.md21
-rw-r--r--docs/administration/CLI_tasks/user.md302
-rw-r--r--docs/administration/backup.md41
-rw-r--r--docs/administration/updating.md27
-rw-r--r--docs/clients.md124
-rw-r--r--docs/configuration/auth.md1
-rw-r--r--docs/configuration/cheatsheet.md1194
-rw-r--r--docs/configuration/custom_emoji.md69
-rw-r--r--docs/configuration/hardening.md103
-rw-r--r--docs/configuration/how_to_serve_another_domain_for_webfinger.md62
-rw-r--r--docs/configuration/howto_database_config.md155
-rw-r--r--docs/configuration/howto_ejabberd.md136
-rw-r--r--docs/configuration/howto_mediaproxy.md34
-rw-r--r--docs/configuration/howto_mongooseim.md10
-rw-r--r--docs/configuration/howto_proxy.md12
-rw-r--r--docs/configuration/howto_search_cjk.md42
-rw-r--r--docs/configuration/howto_set_richmedia_cache_ttl_based_on_image.md33
-rw-r--r--docs/configuration/howto_theming_your_instance.md74
-rw-r--r--docs/configuration/i2p.md196
-rw-r--r--docs/configuration/mrf.md158
-rw-r--r--docs/configuration/onion_federation.md159
-rw-r--r--docs/configuration/optimizing_beam.md66
-rw-r--r--docs/configuration/postgresql.md48
-rw-r--r--docs/configuration/static_dir.md95
-rw-r--r--docs/configuration/storing_remote_media.md38
-rw-r--r--docs/development/API/admin_api.md1751
-rw-r--r--docs/development/API/chats.md255
-rw-r--r--docs/development/API/differences_in_mastoapi_responses.md390
-rw-r--r--docs/development/API/nodeinfo.md347
-rw-r--r--docs/development/API/pleroma_api.md766
-rw-r--r--docs/development/API/prometheus.md44
-rw-r--r--docs/development/ap_extensions.md65
-rw-r--r--docs/development/authentication_authorization.md21
-rw-r--r--docs/development/index.md1
-rw-r--r--docs/development/setting_up_a_gitlab_runner.md9
-rw-r--r--docs/development/setting_up_pleroma_dev.md70
-rw-r--r--docs/index.md19
-rw-r--r--docs/installation/alpine_linux_en.md228
-rw-r--r--docs/installation/arch_linux_en.md221
-rw-r--r--docs/installation/debian_based_en.md184
-rw-r--r--docs/installation/debian_based_jp.md195
-rw-r--r--docs/installation/freebsd_en.md218
-rw-r--r--docs/installation/further_reading.include5
-rw-r--r--docs/installation/generic_dependencies.include16
-rw-r--r--docs/installation/gentoo_en.md302
-rw-r--r--docs/installation/migrating_from_source_otp_en.md143
-rw-r--r--docs/installation/netbsd_en.md211
-rw-r--r--docs/installation/nixos_en.md15
-rw-r--r--docs/installation/openbsd_en.md259
-rw-r--r--docs/installation/openbsd_fi.md121
-rw-r--r--docs/installation/optional/media_graphics_packages.md33
-rw-r--r--docs/installation/otp_en.md308
-rw-r--r--docs/installation/otp_vs_from_source.include3
-rw-r--r--docs/installation/otp_vs_from_source_source.include3
-rw-r--r--docs/installation/yunohost_en.md9
-rwxr-xr-xdocs/installation_1/apache-cache-purge.sh.example36
-rw-r--r--docs/installation_1/caddyfile-pleroma.example17
-rwxr-xr-xdocs/installation_1/freebsd/rc.d/pleroma27
-rwxr-xr-xdocs/installation_1/init.d/pleroma45
-rwxr-xr-xdocs/installation_1/netbsd/rc.d/pleroma57
-rwxr-xr-xdocs/installation_1/nginx-cache-purge.sh.example40
-rw-r--r--docs/installation_1/openbsd/httpd.conf36
-rwxr-xr-xdocs/installation_1/openbsd/rc.d/pleromad34
-rw-r--r--docs/installation_1/openbsd/relayd.conf44
-rw-r--r--docs/installation_1/pleroma-apache.conf84
-rwxr-xr-xdocs/installation_1/pleroma-mongooseim.cfg936
-rw-r--r--docs/installation_1/pleroma.nginx109
-rw-r--r--docs/installation_1/pleroma.service36
-rw-r--r--docs/installation_1/pleroma.supervisord21
-rw-r--r--docs/installation_1/pleroma.vcl127
81 files changed, 11750 insertions, 0 deletions
diff --git a/docs/administration/CLI_tasks/config.md b/docs/administration/CLI_tasks/config.md
new file mode 100644
index 0000000..fc9f3cb
--- /dev/null
+++ b/docs/administration/CLI_tasks/config.md
@@ -0,0 +1,157 @@
+# Transfering the config to/from the database
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Transfer config from file to DB.
+
+!!! note
+ You need to add the following to your config before executing this command:
+
+ ```elixir
+ config :pleroma, configurable_from_database: true
+ ```
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config migrate_to_db
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config migrate_to_db
+ ```
+
+## Transfer config from DB to `config/env.exported_from_db.secret.exs`
+
+!!! note
+ In-Database configuration will still be applied after executing this command unless you set the following in your config:
+
+ ```elixir
+ config :pleroma, configurable_from_database: false
+ ```
+
+Options:
+
+- `<path>` - where to save migrated config. E.g. `--path=/tmp`. If file saved into non standart folder, you must manually copy file into directory where Pleroma can read it. For OTP install path will be `PLEROMA_CONFIG_PATH` or `/etc/pleroma`. For installation from source - `config` directory in the pleroma folder.
+- `<env>` - environment, for which is migrated config. By default is `prod`.
+- To delete transferred settings from database optional flag `-d` can be used
+
+=== "OTP"
+ ```sh
+ ./bin/pleroma_ctl config migrate_from_db [--env=<env>] [-d] [--path=<path>]
+ ```
+
+=== "From Source"
+ ```sh
+ mix pleroma.config migrate_from_db [--env=<env>] [-d] [--path=<path>]
+ ```
+
+## Dump all of the config settings defined in the database
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config dump
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config dump
+ ```
+
+## List individual configuration groups in the database
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config groups
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config groups
+ ```
+
+## Dump the saved configuration values for a specific group or key
+
+e.g., this shows all the settings under `config :pleroma`
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config dump pleroma
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config dump pleroma
+ ```
+
+To get values under a specific key:
+
+e.g., this shows all the settings under `config :pleroma, :instance`
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config dump pleroma instance
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config dump pleroma instance
+ ```
+
+## Delete the saved configuration values for a specific group or key
+
+e.g., this deletes all the settings under `config :tesla`
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config delete [--force] tesla
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config delete [--force] tesla
+ ```
+
+To delete values under a specific key:
+
+e.g., this deletes all the settings under `config :phoenix, :stacktrace_depth`
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config delete [--force] phoenix stacktrace_depth
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config delete [--force] phoenix stacktrace_depth
+ ```
+
+## Remove all settings from the database
+
+This forcibly removes all saved values in the database.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl config [--force] reset
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.config [--force] reset
+ ```
diff --git a/docs/administration/CLI_tasks/database.md b/docs/administration/CLI_tasks/database.md
new file mode 100644
index 0000000..c53c499
--- /dev/null
+++ b/docs/administration/CLI_tasks/database.md
@@ -0,0 +1,161 @@
+# Database maintenance tasks
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+!!! danger
+ These mix tasks can take a long time to complete. Many of them were written to address specific database issues that happened because of bugs in migrations or other specific scenarios. Do not run these tasks "just in case" if everything is fine your instance.
+
+## Replace embedded objects with their references
+
+Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once if the instance was created before Pleroma 1.0.5. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database remove_embedded_objects [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database remove_embedded_objects [option ...]
+ ```
+
+
+### Options
+- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
+
+## Prune old remote posts from the database
+
+This will prune remote posts older than 90 days (configurable with [`config :pleroma, :instance, remote_post_retention_days`](../../configuration/cheatsheet.md#instance)) from the database, they will be refetched from source when accessed.
+
+!!! danger
+ The disk space will only be reclaimed after `VACUUM FULL`. You may run out of disk space during the execution of the task or vacuuming if you don't have about 1/3rds of the database size free.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database prune_objects [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database prune_objects [option ...]
+ ```
+
+### Options
+- `--vacuum` - run `VACUUM FULL` after the objects are pruned
+
+## Create a conversation for all existing DMs
+
+Can be safely re-run
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database bump_all_conversations
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database bump_all_conversations
+ ```
+
+## Remove duplicated items from following and update followers count for all users
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database update_users_following_followers_counts
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database update_users_following_followers_counts
+ ```
+
+## Fix the pre-existing "likes" collections for all objects
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database fix_likes_collections
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database fix_likes_collections
+ ```
+
+## Vacuum the database
+
+### Analyze
+
+Running an `analyze` vacuum job can improve performance by updating statistics used by the query planner. **It is safe to cancel this.**
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database vacuum analyze
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database vacuum analyze
+ ```
+
+### Full
+
+Running a `full` vacuum job rebuilds your entire database by reading all of the data and rewriting it into smaller
+and more compact files with an optimized layout. This process will take a long time and use additional disk space as
+it builds the files side-by-side the existing database files. It can make your database faster and use less disk space,
+but should only be run if necessary. **It is safe to cancel this.**
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database vacuum full
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database vacuum full
+ ```
+
+## Add expiration to all local statuses
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database ensure_expiration
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database ensure_expiration
+ ```
+
+## Change Text Search Configuration
+
+Change `default_text_search_config` for database and (if necessary) text_search_config used in index, then rebuild index (it may take time).
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database set_text_search_config english
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database set_text_search_config english
+ ```
+
+See [PostgreSQL documentation](https://www.postgresql.org/docs/current/textsearch-configuration.html) and `docs/configuration/howto_search_cjk.md` for more detail.
diff --git a/docs/administration/CLI_tasks/digest.md b/docs/administration/CLI_tasks/digest.md
new file mode 100644
index 0000000..a590581
--- /dev/null
+++ b/docs/administration/CLI_tasks/digest.md
@@ -0,0 +1,33 @@
+# Managing digest emails
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Send digest email since given date (user registration date by default) ignoring user activity status.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl digest test <nickname> [since_date]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.digest test <nickname> [since_date]
+ ```
+
+
+Example:
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl digest test donaldtheduck 2019-05-20
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.digest test donaldtheduck 2019-05-20
+ ```
+
diff --git a/docs/administration/CLI_tasks/email.md b/docs/administration/CLI_tasks/email.md
new file mode 100644
index 0000000..2bb57be
--- /dev/null
+++ b/docs/administration/CLI_tasks/email.md
@@ -0,0 +1,45 @@
+# EMail administration tasks
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Send test email (instance email by default)
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl email test [--to <destination email address>]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.email test [--to <destination email address>]
+ ```
+
+Example:
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl email test --to root@example.org
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.email test --to root@example.org
+ ```
+
+## Send confirmation emails to all unconfirmed user accounts
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl email resend_confirmation_emails
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.email resend_confirmation_emails
+ ```
diff --git a/docs/administration/CLI_tasks/emoji.md b/docs/administration/CLI_tasks/emoji.md
new file mode 100644
index 0000000..e3d1b21
--- /dev/null
+++ b/docs/administration/CLI_tasks/emoji.md
@@ -0,0 +1,61 @@
+# Managing emoji packs
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Lists emoji packs and metadata specified in the manifest
+
+=== "OTP"
+ ```sh
+ ./bin/pleroma_ctl emoji ls-packs [option ...]
+ ```
+
+=== "From Source"
+ ```sh
+ mix pleroma.emoji ls-packs [option ...]
+ ```
+
+
+### Options
+- `-m, --manifest PATH/URL` - path to a custom manifest, it can either be an URL starting with `http`, in that case the manifest will be fetched from that address, or a local path
+
+## Fetch, verify and install the specified packs from the manifest into `STATIC-DIR/emoji/PACK-NAME`
+
+=== "OTP"
+ ```sh
+ ./bin/pleroma_ctl emoji get-packs [option ...] <pack ...>
+ ```
+
+=== "From Source"
+ ```sh
+ mix pleroma.emoji get-packs [option ...] <pack ...>
+ ```
+
+### Options
+- `-m, --manifest PATH/URL` - same as [`ls-packs`](#ls-packs)
+
+## Create a new manifest entry and a file list from the specified remote pack file
+
+=== "OTP"
+ ```sh
+ ./bin/pleroma_ctl emoji gen-pack PACK-URL
+ ```
+
+=== "From Source"
+ ```sh
+ mix pleroma.emoji gen-pack PACK-URL
+ ```
+
+Currently, only .zip archives are recognized as remote pack files and packs are therefore assumed to be zip archives. This command is intended to run interactively and will first ask you some basic questions about the pack, then download the remote file and generate an SHA256 checksum for it, then generate an emoji file list for you.
+
+ The manifest entry will either be written to a newly created `pack_name.json` file (pack name is asked in questions) or appended to the existing one, *replacing* the old pack with the same name if it was in the file previously.
+
+ The file list will be written to the file specified previously, *replacing* that file. You _should_ check that the file list doesn't contain anything you don't need in the pack, that is, anything that is not an emoji (the whole pack is downloaded, but only emoji files are extracted).
+
+## Reload emoji packs
+
+=== "OTP"
+ ```sh
+ ./bin/pleroma_ctl emoji reload
+ ```
+
+This command only works with OTP releases.
diff --git a/docs/administration/CLI_tasks/frontend.md b/docs/administration/CLI_tasks/frontend.md
new file mode 100644
index 0000000..4e9d9ee
--- /dev/null
+++ b/docs/administration/CLI_tasks/frontend.md
@@ -0,0 +1,96 @@
+# Managing frontends
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]
+ ```
+
+Frontend can be installed either from local zip file, or automatically downloaded from the web.
+
+You can give all the options directly on the command line, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
+
+Currently, known `<frontend>` values are:
+
+- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
+- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
+- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
+- [fedi-fe](https://git.pleroma.social/pleroma/fedi-fe)
+- [soapbox](https://gitlab.com/soapbox-pub/soapbox)
+
+You can still install frontends that are not configured, see below.
+
+## Example installations for a known frontend
+
+For a frontend configured under the `available` key, it's enough to install it by name.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install pleroma
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install pleroma
+ ```
+
+This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
+
+You can override any of the details. To install a pleroma build from a different URL, you could do this:
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
+ ```
+
+Similarly, you can also install from a local zip file.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+ ```
+
+The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
+
+Careful: This folder will be completely replaced on installation.
+
+## Example installation for an unknown frontend
+
+The installation process is the same, but you will have to give all the needed options on the command line. For example:
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+ ```
+
+If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
+
diff --git a/docs/administration/CLI_tasks/general_cli_task_info.include b/docs/administration/CLI_tasks/general_cli_task_info.include
new file mode 100644
index 0000000..a1ff1da
--- /dev/null
+++ b/docs/administration/CLI_tasks/general_cli_task_info.include
@@ -0,0 +1,5 @@
+Every command should be ran as the `pleroma` user from it's home directory. For example if you are superuser, you would have to wrap the command in `su pleroma -s $SHELL -lc "$COMMAND"`.
+
+??? note "From source note about `MIX_ENV`"
+
+ The `mix` command should be prefixed with the name of environment your Pleroma server is running in, usually it's `MIX_ENV=prod`
diff --git a/docs/administration/CLI_tasks/instance.md b/docs/administration/CLI_tasks/instance.md
new file mode 100644
index 0000000..88509cf
--- /dev/null
+++ b/docs/administration/CLI_tasks/instance.md
@@ -0,0 +1,45 @@
+# Managing instance configuration
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Generate a new configuration file
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl instance gen [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.instance gen [option ...]
+ ```
+
+
+If any of the options are left unspecified, you will be prompted interactively.
+
+### Options
+- `-f`, `--force` - overwrite any output files
+- `-o <path>`, `--output <path>` - the output file for the generated configuration
+- `--output-psql <path>` - the output file for the generated PostgreSQL setup
+- `--domain <domain>` - the domain of your instance
+- `--instance-name <instance_name>` - the name of your instance
+- `--admin-email <email>` - the email address of the instance admin
+- `--notify-email <email>` - email address for notifications
+- `--dbhost <hostname>` - the hostname of the PostgreSQL database to use
+- `--dbname <database_name>` - the name of the database to use
+- `--dbuser <username>` - the user (aka role) to use for the database connection
+- `--dbpass <password>` - the password to use for the database connection
+- `--rum <Y|N>` - Whether to enable RUM indexes
+- `--indexable <Y|N>` - Allow/disallow indexing site by search engines
+- `--db-configurable <Y|N>` - Allow/disallow configuring instance from admin part
+- `--uploads-dir <path>` - the directory uploads go in when using a local uploader
+- `--static-dir <path>` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
+- `--listen-ip <ip>` - the ip the app should listen to, defaults to 127.0.0.1
+- `--listen-port <port>` - the port the app should listen to, defaults to 4000
+- `--strip-uploads-location <Y|N>` - use ExifTool to strip uploads of sensitive location data
+- `--read-uploads-description <Y|N>` - use ExifTool to read image descriptions from uploads
+- `--anonymize-uploads <Y|N>` - randomize uploaded filenames
+- `--dedupe-uploads <Y|N>` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames
+- `--skip-release-env` - skip generation the release environment file
+- `--release-env-file` - release environment file path
diff --git a/docs/administration/CLI_tasks/oauth_app.md b/docs/administration/CLI_tasks/oauth_app.md
new file mode 100644
index 0000000..f056849
--- /dev/null
+++ b/docs/administration/CLI_tasks/oauth_app.md
@@ -0,0 +1,20 @@
+# Creating trusted OAuth App
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Create trusted OAuth App.
+
+Optional params:
+ * `-s SCOPES` - scopes for app, e.g. `read,write,follow,push`.
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.app create -n APP_NAME -r REDIRECT_URI
+ ``` \ No newline at end of file
diff --git a/docs/administration/CLI_tasks/relay.md b/docs/administration/CLI_tasks/relay.md
new file mode 100644
index 0000000..bdd7e8b
--- /dev/null
+++ b/docs/administration/CLI_tasks/relay.md
@@ -0,0 +1,45 @@
+# Managing relays
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Follow a relay
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl relay follow <relay_url>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.relay follow <relay_url>
+ ```
+
+## Unfollow a remote relay
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl relay unfollow <relay_url>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.relay unfollow <relay_url>
+ ```
+
+## List relay subscriptions
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl relay list
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.relay list
+ ```
diff --git a/docs/administration/CLI_tasks/robots_txt.md b/docs/administration/CLI_tasks/robots_txt.md
new file mode 100644
index 0000000..7eeedf5
--- /dev/null
+++ b/docs/administration/CLI_tasks/robots_txt.md
@@ -0,0 +1,21 @@
+# Managing robots.txt
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Generate a new robots.txt file and add it to the static directory
+
+The `robots.txt` that ships by default is permissive. It allows well-behaved search engines to index all of your instance's URIs.
+
+If you want to generate a restrictive `robots.txt`, you can run the following mix task. The generated `robots.txt` will be written in your instance [static directory](../../../configuration/static_dir/).
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl robots_txt disallow_all
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.robots_txt disallow_all
+ ```
diff --git a/docs/administration/CLI_tasks/uploads.md b/docs/administration/CLI_tasks/uploads.md
new file mode 100644
index 0000000..8585ec7
--- /dev/null
+++ b/docs/administration/CLI_tasks/uploads.md
@@ -0,0 +1,21 @@
+# Managing uploads
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Migrate uploads from local to remote storage
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl uploads migrate_local <target_uploader> [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.uploads migrate_local <target_uploader> [option ...]
+ ```
+
+### Options
+- `--delete` - delete local uploads after migrating them to the target uploader
+
+A list of available uploaders can be seen in [Configuration Cheat Sheet](../../configuration/cheatsheet.md#pleromaupload)
diff --git a/docs/administration/CLI_tasks/user.md b/docs/administration/CLI_tasks/user.md
new file mode 100644
index 0000000..24fdaea
--- /dev/null
+++ b/docs/administration/CLI_tasks/user.md
@@ -0,0 +1,302 @@
+# Managing users
+
+{! backend/administration/CLI_tasks/general_cli_task_info.include !}
+
+## Create a user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user new <nickname> <email> [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user new <nickname> <email> [option ...]
+ ```
+
+
+### Options
+- `--name <name>` - the user's display name
+- `--bio <bio>` - the user's bio
+- `--password <password>` - the user's password
+- `--moderator`/`--no-moderator` - whether the user should be a moderator
+- `--admin`/`--no-admin` - whether the user should be an admin
+- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
+
+## List local users
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user list
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user list
+ ```
+
+
+## Generate an invite link
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user invite [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user invite [option ...]
+ ```
+
+
+### Options
+- `--expires-at DATE` - last day on which token is active (e.g. "2019-04-05")
+- `--max-use NUMBER` - maximum numbers of token uses
+
+## List generated invites
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user invites
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user invites
+ ```
+
+
+## Revoke invite
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user revoke_invite <token>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user revoke_invite <token>
+ ```
+
+
+## Delete a user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user rm <nickname>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user rm <nickname>
+ ```
+
+
+## Delete user's posts and interactions
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user delete_activities <nickname>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user delete_activities <nickname>
+ ```
+
+
+## Sign user out from all applications (delete user's OAuth tokens and authorizations)
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user sign_out <nickname>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user sign_out <nickname>
+ ```
+
+## Activate a user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user activate NICKNAME
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user activate NICKNAME
+ ```
+
+## Deactivate a user and unsubscribes local users from the user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user deactivate NICKNAME
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user deactivate NICKNAME
+ ```
+
+
+## Deactivate all accounts from an instance and unsubscribe local users on it
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user deactivate_all_from_instance <instance>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user deactivate_all_from_instance <instance>
+ ```
+
+
+## Create a password reset link for user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user reset_password <nickname>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user reset_password <nickname>
+ ```
+
+
+## Disable Multi Factor Authentication (MFA/2FA) for a user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user reset_mfa <nickname>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user reset_mfa <nickname>
+ ```
+
+
+## Set the value of the given user's settings
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user set <nickname> [option ...]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user set <nickname> [option ...]
+ ```
+
+### Options
+- `--admin`/`--no-admin` - whether the user should be an admin
+- `--confirmed`/`--no-confirmed` - whether the user account is confirmed
+- `--locked`/`--no-locked` - whether the user should be locked
+- `--moderator`/`--no-moderator` - whether the user should be a moderator
+
+## Add tags to a user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user tag <nickname> <tags>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user tag <nickname> <tags>
+ ```
+
+
+## Delete tags from a user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user untag <nickname> <tags>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user untag <nickname> <tags>
+ ```
+
+
+## Toggle confirmation status of the user
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user confirm <nickname>
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user confirm <nickname>
+ ```
+
+## Set confirmation status for all regular active users
+*Admins and moderators are excluded*
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user confirm_all
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user confirm_all
+ ```
+
+## Revoke confirmation status for all regular active users
+*Admins and moderators are excluded*
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl user unconfirm_all
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.user unconfirm_all
+ ```
diff --git a/docs/administration/backup.md b/docs/administration/backup.md
new file mode 100644
index 0000000..5f279ab
--- /dev/null
+++ b/docs/administration/backup.md
@@ -0,0 +1,41 @@
+# Backup/Restore/Move/Remove your instance
+
+## Backup
+
+1. Stop the Pleroma service.
+2. Go to the working directory of Pleroma (default is `/opt/pleroma`)
+3. Run `sudo -Hu postgres pg_dump -d <pleroma_db> --format=custom -f </path/to/backup_location/pleroma.pgdump>` (make sure the postgres user has write access to the destination file)
+4. Copy `pleroma.pgdump`, `config/prod.secret.exs`, `config/setup_db.psql` (if still available) and the `uploads` folder to your backup destination. If you have other modifications, copy those changes too.
+5. Restart the Pleroma service.
+
+## Restore/Move
+
+1. Optionally reinstall Pleroma (either on the same server or on another server if you want to move servers).
+2. Stop the Pleroma service.
+3. Go to the working directory of Pleroma (default is `/opt/pleroma`)
+4. Copy the above mentioned files back to their original position.
+5. Drop the existing database and user if restoring in-place. `sudo -Hu postgres psql -c 'DROP DATABASE <pleroma_db>;';` `sudo -Hu postgres psql -c 'DROP USER <pleroma_db>;'`
+6. Restore the database schema and pleroma postgres role the with the original `setup_db.psql` if you have it: `sudo -Hu postgres psql -f config/setup_db.psql`.
+
+ Alternatively, run the `mix pleroma.instance gen` task again. You can ignore most of the questions, but make the database user, name, and password the same as found in your backup of `config/prod.secret.exs`. Then run the restoration of the pleroma role and schema with of the generated `config/setup_db.psql` as instructed above. You may delete the `config/generated_config.exs` file as it is not needed.
+
+7. Now restore the Pleroma instance's data into the empty database schema: `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
+8. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
+9. Restart the Pleroma service.
+10. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries.
+11. If setting up on a new server configure Nginx by using the `installation/pleroma.nginx` config sample or reference the Pleroma installation guide for your OS which contains the Nginx configuration instructions.
+
+[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
+
+## Remove
+
+1. Optionally you can remove the users of your instance. This will trigger delete requests for their accounts and posts. Note that this is 'best effort' and doesn't mean that all traces of your instance will be gone from the fediverse.
+ * You can do this from the admin-FE where you can select all local users and delete the accounts using the *Moderate multiple users* dropdown.
+ * You can also list local users and delete them individualy using the CLI tasks for [Managing users](./CLI_tasks/user.md).
+2. Stop the Pleroma service `systemctl stop pleroma`
+3. Disable pleroma from systemd `systemctl disable pleroma`
+4. Remove the files and folders you created during installation (see installation guide). This includes the pleroma, nginx and systemd files and folders.
+5. Reload nginx now that the configuration is removed `systemctl reload nginx`
+6. Remove the database and database user `sudo -Hu postgres psql -c 'DROP DATABASE <pleroma_db>;';` `sudo -Hu postgres psql -c 'DROP USER <pleroma_db>;'`
+7. Remove the system user `userdel pleroma`
+8. Remove the dependencies that you don't need anymore (see installation guide). Make sure you don't remove packages that are still needed for other software that you have running!
diff --git a/docs/administration/updating.md b/docs/administration/updating.md
new file mode 100644
index 0000000..00eca36
--- /dev/null
+++ b/docs/administration/updating.md
@@ -0,0 +1,27 @@
+# Updating your instance
+
+You should **always check the [release notes/changelog](https://git.pleroma.social/pleroma/pleroma/-/releases)** in case there are config deprecations, special update steps, etc.
+
+Besides that, doing the following is generally enough:
+
+## For OTP installations
+
+```sh
+# Download the new release
+su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
+
+# Migrate the database, you are advised to stop the instance before doing that
+su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
+```
+
+## For from source installations (using git)
+
+1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
+2. Run `git checkout <tagged release>` [^1]. e.g. `git checkout v2.4.5` This pulls the [tagged release](https://git.pleroma.social/pleroma/pleroma/-/releases) from upstream.
+3. Run `mix deps.get` [^1]. This pulls in any new dependencies.
+4. Stop the Pleroma service.
+5. Run `mix ecto.migrate` [^1] [^2]. This task performs database migrations, if there were any.
+6. Start the Pleroma service.
+
+[^1]: Depending on which install guide you followed (for example on Debian/Ubuntu), you want to run `git` and `mix` tasks as `pleroma` user by adding `sudo -Hu pleroma` before the command.
+[^2]: Prefix with `MIX_ENV=prod` to run it using the production config file.
diff --git a/docs/clients.md b/docs/clients.md
new file mode 100644
index 0000000..31d2d27
--- /dev/null
+++ b/docs/clients.md
@@ -0,0 +1,124 @@
+# Pleroma Clients
+Note: Additional clients may be working but theses are officially supporting Pleroma.
+Feel free to contact us to be added to this list!
+
+## Desktop
+### Roma for Desktop
+- Homepage: <https://www.pleroma.com/#desktopApp>
+- Source Code: <https://github.com/roma-apps/roma-desktop>
+- Platforms: Windows, Mac, Linux
+- Features: MastoAPI, Streaming Ready
+
+### Social
+- Source Code: <https://gitlab.gnome.org/World/Social>
+- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
+- Platforms: Linux (GNOME)
+- Note(2019-01-28): Not at a pre-alpha stage yet
+- Features: MastoAPI
+
+### Whalebird
+- Homepage: <https://whalebird.social/>
+- Source Code: <https://github.com/h3poteto/whalebird-desktop>
+- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)
+- Platforms: Windows, Mac, Linux
+- Features: MastoAPI, Streaming Ready
+
+## Handheld
+### AndStatus
+- Homepage: <http://andstatus.org/>
+- Source Code: <https://github.com/andstatus/andstatus/>
+- Platforms: Android
+- Features: MastoAPI, ActivityPub (Client-to-Server)
+
+### Amaroq
+- Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200>
+- Source Code: <https://github.com/ReticentJohn/Amaroq>
+- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
+- Platforms: iOS
+- Features: MastoAPI, No Streaming
+
+### Fedilab
+- Homepage: <https://fedilab.app/>
+- Source Code: <https://framagit.org/tom79/fedilab/>
+- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
+- Platforms: Android
+- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
+
+### Kyclos
+- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
+- Platforms: SailfishOS
+- Features: MastoAPI, No Streaming
+
+### Husky
+- Source code: <https://git.mentality.rip/FWGS/Husky>
+- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
+- Platforms: Android
+- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
+
+### Fedi
+- Homepage: <https://www.fediapp.com/>
+- Source Code: Proprietary, but gratis
+- Platforms: iOS, Android
+- Features: MastoAPI, Pleroma-specific features like Reactions
+
+### Tusky
+- Homepage: <https://tuskyapp.github.io/>
+- Source Code: <https://github.com/tuskyapp/Tusky>
+- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)
+- Platforms: Android
+- Features: MastoAPI, No Streaming
+
+### Twidere
+- Homepage: <https://twidere.mariotaku.org/>
+- Source Code: <https://github.com/TwidereProject/Twidere-Android/>
+- Contact: <me@mariotaku.org>
+- Platform: Android
+- Features: MastoAPI, No Streaming
+
+### Indigenous
+- Homepage: <https://indigenous.realize.be/>
+- Source Code: <https://github.com/swentel/indigenous-android/>
+- Contact: [@swentel@realize.be](https://realize.be)
+- Platforms: Android
+- Features: MastoAPI, No Streaming
+
+## Alternative Web Interfaces
+### Brutaldon
+- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
+- Source Code: <https://git.carcosa.net/jmcbray/brutaldon>
+- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
+- Features: MastoAPI, No Streaming
+
+### Halcyon
+- Source Code: <https://notabug.org/halcyon-suite/halcyon>
+- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
+- Features: MastoAPI, Streaming Ready
+
+### Pinafore
+- Homepage: <https://pinafore.social/>
+- Source Code: <https://github.com/nolanlawson/pinafore>
+- Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)
+- Note: Pleroma support is a secondary goal
+- Features: MastoAPI, No Streaming
+
+### Sengi
+- Homepage: <https://nicolasconstant.github.io/sengi/>
+- Source Code: <https://github.com/NicolasConstant/sengi>
+- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
+- Features: MastoAPI
+
+### DashFE
+- Source Code: <https://notabug.org/daisuke/DashboardFE>
+- Contact: [@dashfe@stereophonic.space](https://stereophonic.space/users/dashfe)
+
+### BloatFE
+- Source Code: <https://git.freesoftwareextremist.com/bloat/>
+- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
+- Features: Does not requires JavaScript
+- Features: MastoAPI
+
+### Glitch-lily
+- Source Code: <https://lily.kazv.moe/infra/glitch-lily>
+- Contact: [@tusooa@kazv.moe](https://kazv.moe/users/tusooa)
+- Features: MastoAPI
+- Based on [glitch-soc](https://github.com/glitch-soc/mastodon) frontend
diff --git a/docs/configuration/auth.md b/docs/configuration/auth.md
new file mode 100644
index 0000000..c80f094
--- /dev/null
+++ b/docs/configuration/auth.md
@@ -0,0 +1 @@
+See `Authentication` section of [the configuration cheatsheet](../configuration/cheatsheet.md#authentication).
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
new file mode 100644
index 0000000..bbdf30a
--- /dev/null
+++ b/docs/configuration/cheatsheet.md
@@ -0,0 +1,1194 @@
+# Configuration Cheat Sheet
+
+This is a cheat sheet for Pleroma configuration file, any setting possible to configure should be listed here.
+
+For OTP installations the configuration is typically stored in `/etc/pleroma/config.exs`.
+
+For from source installations Pleroma configuration works by first importing the base config `config/config.exs`, then overriding it by the environment config `config/$MIX_ENV.exs` and then overriding it by user config `config/$MIX_ENV.secret.exs`. In from source installations you should always make the changes to the user config and NEVER to the base config to avoid breakages and merge conflicts. So for production you change/add configuration to `config/prod.secret.exs`.
+
+To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs). You can also use this file if you don't know how an option is supposed to be formatted.
+
+## :shout
+
+* `enabled` - Enables the backend Shoutbox chat feature. Defaults to `true`.
+* `limit` - Shout character limit. Defaults to `5_000`
+
+## :instance
+* `name`: The instance’s name.
+* `email`: Email used to reach an Administrator/Moderator of the instance.
+* `notify_email`: Email used for notifications.
+* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``.
+* `short_description`: Shorter version of instance description, can be seen on ``/api/v1/instance``.
+* `limit`: Posts character limit (CW/Subject included in the counter).
+* `description_limit`: The character limit for image descriptions.
+* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
+* `upload_limit`: File size limit of uploads (except for avatar, background, banner).
+* `avatar_upload_limit`: File size limit of user’s profile avatars.
+* `background_upload_limit`: File size limit of user’s profile backgrounds.
+* `banner_upload_limit`: File size limit of user’s profile banners.
+* `poll_limits`: A map with poll limits for **local** polls.
+ * `max_options`: Maximum number of options.
+ * `max_option_chars`: Maximum number of characters per option.
+ * `min_expiration`: Minimum expiration time (in seconds).
+ * `max_expiration`: Maximum expiration time (in seconds).
+* `registrations_open`: Enable registrations for anyone, invitations can be enabled when false.
+* `invites_enabled`: Enable user invitations for admins (depends on `registrations_open: false`).
+* `account_activation_required`: Require users to confirm their emails before signing in.
+* `account_approval_required`: Require users to be manually approved by an admin before signing in.
+* `federating`: Enable federation with other instances.
+* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
+* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
+* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
+* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
+* `quarantined_instances`: ActivityPub instances where private (DMs, followers-only) activities will not be send.
+* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
+* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
+ older software for theses nicknames.
+* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
+* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
+* `autofollowing_nicknames`: Set to nicknames of (local) users that automatically follows every newly registered user.
+* `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
+* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`).
+* `report_strip_status`: Strip associated statuses in reports to ids when closed/resolved, otherwise keep a copy.
+* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
+* `healthcheck`: If set to true, system data will be shown on ``/api/v1/pleroma/healthcheck``.
+* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database.
+* `user_bio_length`: A user bio maximum length (default: `5000`).
+* `user_name_length`: A user name maximum length (default: `100`).
+* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
+* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
+* `max_account_fields`: The maximum number of custom fields in the user profile (default: `10`).
+* `max_remote_account_fields`: The maximum number of custom fields in the remote user profile (default: `20`).
+* `account_field_name_length`: An account field name maximum length (default: `512`).
+* `account_field_value_length`: An account field value maximum length (default: `2048`).
+* `registration_reason_length`: Maximum registration reason length (default: `500`).
+* `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
+* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
+* `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`).
+* `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day).
+* `admin_privileges`: A list of privileges an admin has (e.g. delete messages, manage reports...)
+ * Possible values are:
+ * `:users_read`
+ * Allows admins to fetch users through the admin API.
+ * `:users_manage_invites`
+ * Allows admins to manage invites. This includes sending, resending, revoking and approving invites.
+ * `:users_manage_activation_state`
+ * Allows admins to activate and deactivate accounts. This also allows them to see deactivated users through the Mastodon API.
+ * `:users_manage_tags`
+ * Allows admins to set and remove tags for users. This can be useful in combination with MRF policies, such as `Pleroma.Web.ActivityPub.MRF.TagPolicy`.
+ * `:users_manage_credentials`
+ * Allows admins to trigger a password reset and set new credentials for an user.
+ * `:users_delete`
+ * Allows admins to delete accounts. Note that deleting an account is actually deactivating it and removing all data like posts, profile information, etc.
+ * `:messages_read`
+ * Allows admins to read messages through the admin API, including non-public posts and chats.
+ * `:messages_delete`
+ * Allows admins to delete messages from other users.
+ * `:instances_delete,`
+ * Allows admins to remove a whole remote instance from your instance. This will delete all users and messages from that remote instance.
+ * `:reports_manage_reports`
+ * Allows admins to see and manage reports.
+ * `:moderation_log_read,`
+ * Allows admins to read the entries in the moderation log.
+ * `:emoji_manage_emoji`
+ * Allows admins to manage custom emoji on the instance.
+ * `:statistics_read,`
+ * Allows admins to see some simple statistics about the instance.
+* `moderator_privileges`: A list of privileges a moderator has (e.g. delete messages, manage reports...)
+ * Possible values are the same as for `admin_privileges`
+
+## :database
+* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).
+
+## Background migrations
+* `populate_hashtags_table/sleep_interval_ms`: Sleep interval between each chunk of processed records in order to decrease the load on the system (defaults to 0 and should be keep default on most instances).
+* `populate_hashtags_table/fault_rate_allowance`: Max rate of failed objects to actually processed objects in order to enable the feature (any value from 0.0 which tolerates no errors to 1.0 which will enable the feature even if hashtags transfer failed for all records).
+
+## Welcome
+* `direct_message`: - welcome message sent as a direct message.
+ * `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`.
+ * `sender_nickname`: The nickname of the local user that sends the welcome message.
+ * `message`: A message that will be send to a newly registered users as a direct message.
+* `chat_message`: - welcome message sent as a chat message.
+ * `enabled`: Enables the send a chat message to a newly registered user. Defaults to `false`.
+ * `sender_nickname`: The nickname of the local user that sends the welcome message.
+ * `message`: A message that will be send to a newly registered users as a chat message.
+* `email`: - welcome message sent as a email.
+ * `enabled`: Enables the send a welcome email to a newly registered user. Defaults to `false`.
+ * `sender`: The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.
+ * `subject`: A subject of welcome email.
+ * `html`: A html that will be send to a newly registered users as a email.
+ * `text`: A text that will be send to a newly registered users as a email.
+
+ Example:
+
+ ```elixir
+ config :pleroma, :welcome,
+ direct_message: [
+ enabled: true,
+ sender_nickname: "lain",
+ message: "Hi! Welcome on board!"
+ ],
+ email: [
+ enabled: true,
+ sender: {"Pleroma App", "welcome@pleroma.app"},
+ subject: "Welcome to <%= instance_name %>",
+ html: "Welcome to <%= instance_name %>",
+ text: "Welcome to <%= instance_name %>"
+ ]
+ ```
+
+## Message rewrite facility
+
+### :mrf
+* `policies`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
+ * `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default).
+ * `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production.
+ * `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See [`:mrf_simple`](#mrf_simple)).
+ * `Pleroma.Web.ActivityPub.MRF.TagPolicy`: Applies policies to individual users based on tags, which can be set using pleroma-fe/admin-fe/any other app that supports Pleroma Admin API. For example it allows marking posts from individual users nsfw (sensitive).
+ * `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (See [`:mrf_subchain`](#mrf_subchain)).
+ * `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See [`:mrf_rejectnonpublic`](#mrf_rejectnonpublic)).
+ * `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
+ * `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
+ * `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
+ * `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)).
+ * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
+ * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
+ * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections.
+ * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
+ * `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed.
+ * `Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy`: Drops follow requests from followbots. Users can still allow bots to follow them by first following the bot.
+ * `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).
+ * `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content.
+* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
+* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
+
+## Federation
+### MRF policies
+
+!!! note
+ Configuring MRF policies is not enough for them to take effect. You have to enable them by specifying their module in `policies` under [:mrf](#mrf) section.
+
+#### :mrf_simple
+* `media_removal`: List of instances to strip media attachments from and the reason for doing so.
+* `media_nsfw`: List of instances to tag all media as NSFW (sensitive) from and the reason for doing so.
+* `federated_timeline_removal`: List of instances to remove from the Federated Timeline (aka The Whole Known Network) and the reason for doing so.
+* `reject`: List of instances to reject activities (except deletes) from and the reason for doing so.
+* `accept`: List of instances to only accept activities (except deletes) from and the reason for doing so.
+* `followers_only`: Force posts from the given instances to be visible by followers only and the reason for doing so.
+* `report_removal`: List of instances to reject reports from and the reason for doing so.
+* `avatar_removal`: List of instances to strip avatars from and the reason for doing so.
+* `banner_removal`: List of instances to strip banners from and the reason for doing so.
+* `reject_deletes`: List of instances to reject deletions from and the reason for doing so.
+
+#### :mrf_subchain
+This policy processes messages through an alternate pipeline when a given message matches certain criteria.
+All criteria are configured as a map of regular expressions to lists of policy modules.
+
+* `match_actor`: Matches a series of regular expressions against the actor field.
+
+Example:
+
+```elixir
+config :pleroma, :mrf_subchain,
+ match_actor: %{
+ ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
+ }
+```
+
+#### :mrf_rejectnonpublic
+* `allow_followersonly`: whether to allow followers-only posts.
+* `allow_direct`: whether to allow direct messages.
+
+#### :mrf_hellthread
+* `delist_threshold`: Number of mentioned users after which the message gets delisted (the message can still be seen, but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable.
+* `reject_threshold`: Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.
+
+#### :mrf_keyword
+* `reject`: A list of patterns which result in message being rejected, each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html).
+* `federated_timeline_removal`: A list of patterns which result in message being removed from federated timelines (a.k.a unlisted), each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html).
+* `replace`: A list of tuples containing `{pattern, replacement}`, `pattern` can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html).
+
+#### :mrf_mention
+* `actors`: A list of actors, for which to drop any posts mentioning.
+
+#### :mrf_vocabulary
+* `accept`: A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.
+* `reject`: A list of ActivityStreams terms to reject. If empty, no messages are rejected.
+
+#### :mrf_user_allowlist
+
+The keys in this section are the domain names that the policy should apply to.
+Each key should be assigned a list of users that should be allowed through by
+their ActivityPub ID.
+
+An example:
+
+```elixir
+config :pleroma, :mrf_user_allowlist, %{
+ "example.org" => ["https://example.org/users/admin"]
+}
+```
+
+#### :mrf_object_age
+* `threshold`: Required time offset (in seconds) compared to your server clock of an incoming post before actions are taken.
+ e.g., A value of 900 results in any post with a timestamp older than 15 minutes will be acted upon.
+* `actions`: A list of actions to apply to the post:
+ * `:delist` removes the post from public timelines
+ * `:strip_followers` removes followers from the ActivityPub recipient list, ensuring they won't be delivered to home timelines, additionally for followers-only it degrades to a direct message
+ * `:reject` rejects the message entirely
+
+#### :mrf_steal_emoji
+* `hosts`: List of hosts to steal emojis from
+* `rejected_shortcodes`: Regex-list of shortcodes to reject
+* `size_limit`: File size limit (in bytes), checked before an emoji is saved to the disk
+
+#### :mrf_activity_expiration
+
+* `days`: Default global expiration time for all local Create activities (in days)
+
+#### :mrf_hashtag
+
+* `sensitive`: List of hashtags to mark activities as sensitive (default: `nsfw`)
+* `federated_timeline_removal`: List of hashtags to remove activities from the federated timeline (aka TWNK)
+* `reject`: List of hashtags to reject activities from
+
+Notes:
+- The hashtags in the configuration do not have a leading `#`.
+- This MRF Policy is always enabled, if you want to disable it you have to set empty lists
+
+#### :mrf_follow_bot
+
+* `follower_nickname`: The name of the bot account to use for following newly discovered users. Using `followbot` or similar is strongly suggested.
+
+
+### :activitypub
+* `unfollow_blocked`: Whether blocks result in people getting unfollowed
+* `outgoing_blocks`: Whether to federate blocks to other instances
+* `blockers_visible`: Whether a user can see the posts of users who blocked them
+* `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question
+* `sign_object_fetches`: Sign object fetches with HTTP signatures
+* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
+
+## Pleroma.User
+
+* `restricted_nicknames`: List of nicknames users may not register with.
+* `email_blacklist`: List of email domains users may not register with.
+
+## Pleroma.ScheduledActivity
+
+* `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`)
+* `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`)
+* `enabled`: whether scheduled activities are sent to the job queue to be executed
+
+### :frontend_configurations
+
+This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options).
+
+Frontends can access these settings at `/api/v1/pleroma/frontend_configurations`
+
+To add your own configuration for PleromaFE, use it like this:
+
+```elixir
+config :pleroma, :frontend_configurations,
+ pleroma_fe: %{
+ theme: "pleroma-dark",
+ # ... see /priv/static/static/config.json for the available keys.
+}
+```
+
+These settings **need to be complete**, they will override the defaults.
+
+### :static_fe
+
+Render profiles and posts using server-generated HTML that is viewable without using JavaScript.
+
+Available options:
+
+* `enabled` - Enables the rendering of static HTML. Defaults to `false`.
+
+### :assets
+
+This section configures assets to be used with various frontends. Currently the only option
+relates to mascots on the mastodon frontend
+
+* `mascots`: KeywordList of mascots, each element __MUST__ contain both a `url` and a
+ `mime_type` key.
+* `default_mascot`: An element from `mascots` - This will be used as the default mascot
+ on MastoFE (default: `:pleroma_fox_tan`).
+
+### :manifest
+
+This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.
+
+* `icons`: Describe the icons of the app, this a list of maps describing icons in the same way as the
+ [spec](https://www.w3.org/TR/appmanifest/#imageresource-and-its-members) describes it.
+
+ Example:
+
+ ```elixir
+ config :pleroma, :manifest,
+ icons: [
+ %{
+ src: "/static/logo.png"
+ },
+ %{
+ src: "/static/icon.png",
+ type: "image/png"
+ },
+ %{
+ src: "/static/icon.ico",
+ sizes: "72x72 96x96 128x128 256x256"
+ }
+ ]
+ ```
+
+* `theme_color`: Describe the theme color of the app. (Example: `"#282c37"`, `"rebeccapurple"`).
+* `background_color`: Describe the background color of the app. (Example: `"#191b22"`, `"aliceblue"`).
+
+## :emoji
+
+* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
+* `pack_extensions`: A list of file extensions for emojis, when no emoji.txt for a pack is present. Example `[".png", ".gif"]`
+* `groups`: Emojis are ordered in groups (tags). This is an array of key-value pairs where the key is the groupname and the value the location or array of locations. `*` can be used as a wildcard. Example `[Custom: ["/emoji/*.png", "/emoji/custom/*.png"]]`
+* `default_manifest`: Location of the JSON-manifest. This manifest contains information about the emoji-packs you can download. Currently only one manifest can be added (no arrays).
+* `shared_pack_cache_seconds_per_file`: When an emoji pack is shared, the archive is created and cached in
+ memory for this amount of seconds multiplied by the number of files.
+
+## :media_proxy
+
+* `enabled`: Enables proxying of remote media to the instance’s proxy
+* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
+* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
+* `whitelist`: List of hosts with scheme to bypass the mediaproxy (e.g. `https://example.com`)
+* `invalidation`: options for remove media from cache after delete object:
+ * `enabled`: Enables purge cache
+ * `provider`: Which one of the [purge cache strategy](#purge-cache-strategy) to use.
+
+## :media_preview_proxy
+
+* `enabled`: Enables proxying of remote media preview to the instance’s proxy. Requires enabled media proxy (`media_proxy/enabled`).
+* `thumbnail_max_width`: Max width of preview thumbnail for images (video preview always has original dimensions).
+* `thumbnail_max_height`: Max height of preview thumbnail for images (video preview always has original dimensions).
+* `image_quality`: Quality of the output. Ranges from 0 (min quality) to 100 (max quality).
+* `min_content_length`: Min content length to perform preview, in bytes. If greater than 0, media smaller in size will be served as is, without thumbnailing.
+
+### Purge cache strategy
+
+#### Pleroma.Web.MediaProxy.Invalidation.Script
+
+This strategy allow perform external shell script to purge cache.
+Urls of attachments are passed to the script as arguments.
+
+* `script_path`: Path to the external script.
+* `url_format`: Set to `:htcacheclean` if using Apache's htcacheclean utility.
+
+Example:
+
+```elixir
+config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
+ script_path: "./installation/nginx-cache-purge.example"
+```
+
+#### Pleroma.Web.MediaProxy.Invalidation.Http
+
+This strategy allow perform custom http request to purge cache.
+
+* `method`: http method. default is `purge`
+* `headers`: http headers.
+* `options`: request options.
+
+Example:
+```elixir
+config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Http,
+ method: :purge,
+ headers: [],
+ options: []
+```
+
+## Link previews
+
+### Pleroma.Web.Metadata (provider)
+* `providers`: a list of metadata providers to enable. Providers available:
+ * `Pleroma.Web.Metadata.Providers.OpenGraph`
+ * `Pleroma.Web.Metadata.Providers.TwitterCard`
+* `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews.
+
+### :rich_media (consumer)
+* `enabled`: if enabled the instance will parse metadata from attached links to generate link previews.
+* `ignore_hosts`: list of hosts which will be ignored by the metadata parser. For example `["accounts.google.com", "xss.website"]`, defaults to `[]`.
+* `ignore_tld`: list TLDs (top-level domains) which will ignore for parse metadata. default is ["local", "localdomain", "lan"].
+* `parsers`: list of Rich Media parsers.
+* `failure_backoff`: Amount of milliseconds after request failure, during which the request will not be retried.
+
+## HTTP server
+
+### Pleroma.Web.Endpoint
+
+!!! note
+ `Phoenix` endpoint configuration, all configuration options can be viewed [here](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-dynamic-configuration), only common options are listed here.
+
+* `http` - a list containing http protocol configuration, all configuration options can be viewed [here](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html#module-options), only common options are listed here. For deployment using docker, you need to set this to `[ip: {0,0,0,0}, port: 4000]` to make pleroma accessible from other containers (such as your nginx server).
+ - `ip` - a tuple consisting of 4 integers
+ - `port`
+* `url` - a list containing the configuration for generating urls, accepts
+ - `host` - the host without the scheme and a post (e.g `example.com`, not `https://example.com:2020`)
+ - `scheme` - e.g `http`, `https`
+ - `port`
+ - `path`
+* `extra_cookie_attrs` - a list of `Key=Value` strings to be added as non-standard cookie attributes. Defaults to `["SameSite=Lax"]`. See the [SameSite article](https://www.owasp.org/index.php/SameSite) on OWASP for more info.
+
+Example:
+```elixir
+config :pleroma, Pleroma.Web.Endpoint,
+ url: [host: "example.com", port: 2020, scheme: "https"],
+ http: [
+ port: 8080,
+ ip: {127, 0, 0, 1}
+ ]
+```
+
+This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls starting with `https://example.com:2020`
+
+### :http_security
+* ``enabled``: Whether the managed content security policy is enabled.
+* ``sts``: Whether to additionally send a `Strict-Transport-Security` header.
+* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent.
+* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent.
+* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`.
+* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header.
+
+### Pleroma.Web.Plugs.RemoteIp
+
+!!! warning
+ If your instance is not behind at least one reverse proxy, you should not enable this plug.
+
+`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
+
+Available options:
+
+* `enabled` - Enable/disable the plug. Defaults to `false`.
+* `headers` - A list of strings naming the HTTP headers to use when deriving the true client IP address. Defaults to `["x-forwarded-for"]`.
+* `proxies` - A list of upstream proxy IP subnets in CIDR notation from which we will parse the content of `headers`. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128.
+* `reserved` - A list of reserved IP subnets in CIDR notation which should be ignored if found in `headers`. Defaults to `["127.0.0.0/8", "::1/128", "fc00::/7", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]`.
+
+
+### :rate_limit
+
+!!! note
+ If your instance is behind a reverse proxy ensure [`Pleroma.Web.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default).
+
+A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
+
+* The first element: `scale` (Integer). The time scale in milliseconds.
+* The second element: `limit` (Integer). How many requests to limit in the time scale provided.
+
+It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
+
+For example:
+
+```elixir
+config :pleroma, :rate_limit,
+ authentication: {60_000, 15},
+ search: [{1000, 10}, {1000, 30}]
+```
+
+Means that:
+
+1. In 60 seconds, 15 authentication attempts can be performed from the same IP address.
+2. In 1 second, 10 search requests can be performed from the same IP adress by unauthenticated users, while authenticated users can perform 30 search requests per second.
+
+Supported rate limiters:
+
+* `:search` - Account/Status search.
+* `:timeline` - Timeline requests (each timeline has it's own limiter).
+* `:app_account_creation` - Account registration from the API.
+* `:relations_actions` - Following/Unfollowing in general.
+* `:relation_id_action` - Following/Unfollowing for a specific user.
+* `:statuses_actions` - Status actions such as: (un)repeating, (un)favouriting, creating, deleting.
+* `:status_id_action` - (un)Repeating/(un)Favouriting a particular status.
+* `:authentication` - Authentication actions, i.e getting an OAuth token.
+* `:password_reset` - Requesting password reset emails.
+* `:account_confirmation_resend` - Requesting resending account confirmation emails.
+* `:ap_routes` - Requesting statuses via ActivityPub.
+
+### :web_cache_ttl
+
+The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.
+
+Available caches:
+
+* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration).
+* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds).
+
+## HTTP client
+
+### :http
+
+* `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`)
+* `send_user_agent`: should we include a user agent with HTTP requests? (default: `true`)
+* `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default`
+* `adapter`: array of adapter options
+
+### :hackney_pools
+
+Advanced. Tweaks Hackney (http client) connections pools.
+
+There's three pools used:
+
+* `:federation` for the federation jobs.
+ You may want this pool max_connections to be at least equal to the number of federator jobs + retry queue jobs.
+* `:media` for rich media, media proxy
+* `:upload` for uploaded media (if using a remote uploader and `proxy_remote: true`)
+
+For each pool, the options are:
+
+* `max_connections` - how much connections a pool can hold
+* `timeout` - retention duration for connections
+
+
+### :connections_pool
+
+*For `gun` adapter*
+
+Settings for HTTP connection pool.
+
+* `:connection_acquisition_wait` - Timeout to acquire a connection from pool.The total max time is this value multiplied by the number of retries.
+* `connection_acquisition_retries` - Number of attempts to acquire the connection from the pool if it is overloaded. Each attempt is timed `:connection_acquisition_wait` apart.
+* `:max_connections` - Maximum number of connections in the pool.
+* `:connect_timeout` - Timeout to connect to the host.
+* `:reclaim_multiplier` - Multiplied by `:max_connections` this will be the maximum number of idle connections that will be reclaimed in case the pool is overloaded.
+
+### :pools
+
+*For `gun` adapter*
+
+Settings for request pools. These pools are limited on top of `:connections_pool`.
+
+There are four pools used:
+
+* `:federation` for the federation jobs. You may want this pool's max_connections to be at least equal to the number of federator jobs + retry queue jobs.
+* `:media` - for rich media, media proxy.
+* `:upload` - for proxying media when a remote uploader is used and `proxy_remote: true`.
+* `:default` - for other requests.
+
+For each pool, the options are:
+
+* `:size` - limit to how much requests can be concurrently executed.
+* `:recv_timeout` - timeout while `gun` will wait for response
+* `:max_waiting` - limit to how much requests can be waiting for others to finish, after this is reached, subsequent requests will be dropped.
+
+## Captcha
+
+### Pleroma.Captcha
+
+* `enabled`: Whether the captcha should be shown on registration.
+* `method`: The method/service to use for captcha.
+* `seconds_valid`: The time in seconds for which the captcha is valid.
+
+### Captcha providers
+
+#### Pleroma.Captcha.Native
+
+A built-in captcha provider. Enabled by default.
+
+#### Pleroma.Captcha.Kocaptcha
+
+Kocaptcha is a very simple captcha service with a single API endpoint,
+the source code is here: [kocaptcha](https://github.com/koto-bank/kocaptcha). The default endpoint
+`https://captcha.kotobank.ch` is hosted by the developer.
+
+* `endpoint`: the Kocaptcha endpoint to use.
+
+## Uploads
+
+### Pleroma.Upload
+
+* `uploader`: Which one of the [uploaders](#uploaders) to use.
+* `filters`: List of [upload filters](#upload-filters) to use.
+* `link_name`: When enabled Pleroma will add a `name` parameter to the url of the upload, for example `https://instance.tld/media/corndog.png?name=corndog.png`. This is needed to provide the correct filename in Content-Disposition headers when using filters like `Pleroma.Upload.Filter.Dedupe`
+* `base_url`: The base URL to access a user-uploaded file. Useful when you want to host the media files via another domain or are using a 3rd party S3 provider.
+* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
+* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
+* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.
+* `default_description`: Sets which default description an image has if none is set explicitly. Options: nil (default) - Don't set a default, :filename - use the filename of the file, a string (e.g. "attachment") - Use this string
+
+!!! warning
+ `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
+
+### Uploaders
+
+#### Pleroma.Uploaders.Local
+
+* `uploads`: Which directory to store the user-uploads in, relative to pleroma’s working directory.
+
+#### Pleroma.Uploaders.S3
+
+Don't forget to configure [Ex AWS S3](#ex-aws-s3-settings)
+
+* `bucket`: S3 bucket name.
+* `bucket_namespace`: S3 bucket namespace.
+* `truncated_namespace`: If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or "" etc.
+* `streaming_enabled`: Enable streaming uploads, when enabled the file will be sent to the server in chunks as it's being read. This may be unsupported by some providers, try disabling this if you have upload problems.
+
+#### Ex AWS S3 settings
+
+* `access_key_id`: Access key ID
+* `secret_access_key`: Secret access key
+* `host`: S3 host
+
+Example:
+
+```elixir
+config :ex_aws, :s3,
+ access_key_id: "xxxxxxxxxx",
+ secret_access_key: "yyyyyyyyyy",
+ host: "s3.eu-central-1.amazonaws.com"
+```
+
+### Upload filters
+
+#### Pleroma.Upload.Filter.AnonymizeFilename
+
+This filter replaces the filename (not the path) of an upload. For complete obfuscation, add
+`Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename.
+
+* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. You can get the original filename extension by using `{extension}`, for example `custom-file-name.{extension}`.
+
+#### Pleroma.Upload.Filter.Dedupe
+
+No specific configuration.
+
+#### Pleroma.Upload.Filter.Exiftool.StripLocation
+
+This filter only strips the GPS and location metadata with Exiftool leaving color profiles and attributes intact.
+
+No specific configuration.
+
+#### Pleroma.Upload.Filter.Exiftool.ReadDescription
+
+This filter reads the ImageDescription and iptc:Caption-Abstract fields with Exiftool so clients can prefill the media description field.
+
+No specific configuration.
+
+#### Pleroma.Upload.Filter.Mogrify
+
+* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.
+
+## Email
+
+### Pleroma.Emails.Mailer
+* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
+* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
+* `enabled`: Allows enable/disable send emails. Default: `false`.
+
+An example for Sendgrid adapter:
+
+```elixir
+config :pleroma, Pleroma.Emails.Mailer,
+ enabled: true,
+ adapter: Swoosh.Adapters.Sendgrid,
+ api_key: "YOUR_API_KEY"
+```
+
+An example for SMTP adapter:
+
+```elixir
+config :pleroma, Pleroma.Emails.Mailer,
+ enabled: true,
+ adapter: Swoosh.Adapters.SMTP,
+ relay: "smtp.gmail.com",
+ username: "YOUR_USERNAME@gmail.com",
+ password: "YOUR_SMTP_PASSWORD",
+ port: 465,
+ ssl: true,
+ auth: :always
+```
+
+### :email_notifications
+
+Email notifications settings.
+
+ - digest - emails of "what you've missed" for users who have been
+ inactive for a while.
+ - active: globally enable or disable digest emails
+ - schedule: When to send digest email, in [crontab format](https://en.wikipedia.org/wiki/Cron).
+ "0 0 * * 0" is the default, meaning "once a week at midnight on Sunday morning"
+ - interval: Minimum interval between digest emails to one user
+ - inactivity_threshold: Minimum user inactivity threshold
+
+### Pleroma.Emails.UserEmail
+
+- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.
+- `:styling` - a map with color settings for email templates.
+
+### Pleroma.Emails.NewUsersDigestEmail
+
+- `:enabled` - a boolean, enables new users admin digest email when `true`. Defaults to `false`.
+
+## Background jobs
+
+### Oban
+
+[Oban](https://github.com/sorentwo/oban) asynchronous job processor configuration.
+
+Configuration options described in [Oban readme](https://github.com/sorentwo/oban#usage):
+
+* `repo` - app's Ecto repo (`Pleroma.Repo`)
+* `log` - logs verbosity
+* `queues` - job queues (see below)
+* `crontab` - periodic jobs, see [`Oban.Cron`](#obancron)
+
+Pleroma has the following queues:
+
+* `activity_expiration` - Activity expiration
+* `federator_outgoing` - Outgoing federation
+* `federator_incoming` - Incoming federation
+* `mailer` - Email sender, see [`Pleroma.Emails.Mailer`](#pleromaemailsmailer)
+* `transmogrifier` - Transmogrifier
+* `web_push` - Web push notifications
+* `scheduled_activities` - Scheduled activities, see [`Pleroma.ScheduledActivity`](#pleromascheduledactivity)
+
+#### Oban.Cron
+
+Pleroma has these periodic job workers:
+
+* `Pleroma.Workers.Cron.DigestEmailsWorker` - digest emails for users with new mentions and follows
+* `Pleroma.Workers.Cron.NewUsersDigestWorker` - digest emails for admins with new registrations
+
+```elixir
+config :pleroma, Oban,
+ repo: Pleroma.Repo,
+ verbose: false,
+ prune: {:maxlen, 1500},
+ queues: [
+ federator_incoming: 50,
+ federator_outgoing: 50
+ ],
+ crontab: [
+ {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
+ {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
+ ]
+```
+
+This config contains two queues: `federator_incoming` and `federator_outgoing`. Both have the number of max concurrent jobs set to `50`.
+
+#### Migrating `pleroma_job_queue` settings
+
+`config :pleroma_job_queue, :queues` is replaced by `config :pleroma, Oban, :queues` and uses the same format (keys are queues' names, values are max concurrent jobs numbers).
+
+### :workers
+
+Includes custom worker options not interpretable directly by `Oban`.
+
+* `retries` — keyword lists where keys are `Oban` queues (see above) and values are numbers of max attempts for failed jobs.
+
+Example:
+
+```elixir
+config :pleroma, :workers,
+ retries: [
+ federator_incoming: 5,
+ federator_outgoing: 5
+ ]
+```
+
+#### Migrating `Pleroma.Web.Federator.RetryQueue` settings
+
+* `max_retries` is replaced with `config :pleroma, :workers, retries: [federator_outgoing: 5]`
+* `enabled: false` corresponds to `config :pleroma, :workers, retries: [federator_outgoing: 1]`
+* deprecated options: `max_jobs`, `initial_timeout`
+
+## :web_push_encryption, :vapid_details
+
+Web Push Notifications configuration. You can use the mix task `mix web_push.gen.keypair` to generate it.
+
+* ``subject``: a mailto link for the administrative contact. It’s best if this email is not a personal email address, but rather a group email so that if a person leaves an organization, is unavailable for an extended period, or otherwise can’t respond, someone else on the list can.
+* ``public_key``: VAPID public key
+* ``private_key``: VAPID private key
+
+## :logger
+* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
+
+An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
+```elixir
+config :logger,
+ backends: [{ExSyslogger, :ex_syslogger}]
+
+config :logger, :ex_syslogger,
+ level: :warn
+```
+
+Another example, keeping console output and adding the pid to syslog output:
+```elixir
+config :logger,
+ backends: [:console, {ExSyslogger, :ex_syslogger}]
+
+config :logger, :ex_syslogger,
+ level: :warn,
+ option: [:pid, :ndelay]
+```
+
+See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/)
+
+An example of logging info to local syslog, but debug to console:
+```elixir
+config :logger,
+ backends: [ {ExSyslogger, :ex_syslogger}, :console ],
+ level: :info
+
+config :logger, :ex_syslogger,
+ level: :info,
+ ident: "pleroma",
+ format: "$metadata[$level] $message"
+
+config :logger, :console,
+ level: :debug,
+ format: "\n$time $metadata[$level] $message\n",
+ metadata: [:request_id]
+```
+
+
+
+## Database options
+
+### RUM indexing for full text search
+
+!!! warning
+ It is recommended to use PostgreSQL v11 or newer. We have seen some minor issues with lower PostgreSQL versions.
+
+* `rum_enabled`: If RUM indexes should be used. Defaults to `false`.
+
+RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. While they may eventually be mainlined, for now they have to be installed as a PostgreSQL extension from https://github.com/postgrespro/rum.
+
+Their advantage over the standard GIN indexes is that they allow efficient ordering of search results by timestamp, which makes search queries a lot faster on larger servers, by one or two orders of magnitude. They take up around 3 times as much space as GIN indexes.
+
+To enable them, both the `rum_enabled` flag has to be set and the following special migration has to be run:
+
+`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`
+
+This will probably take a long time.
+
+## Alternative client protocols
+
+### BBS / SSH access
+
+To enable simple command line interface accessible over ssh, add a setting like this to your configuration file:
+
+```exs
+app_dir = File.cwd!
+priv_dir = Path.join([app_dir, "priv/ssh_keys"])
+
+config :esshd,
+ enabled: true,
+ priv_dir: priv_dir,
+ handler: "Pleroma.BBS.Handler",
+ port: 10_022,
+ password_authenticator: "Pleroma.BBS.Authenticator"
+```
+
+Feel free to adjust the priv_dir and port number. Then you will have to create the key for the keys (in the example `priv/ssh_keys`) and create the host keys with `ssh-keygen -m PEM -N "" -b 2048 -t rsa -f ssh_host_rsa_key`. After restarting, you should be able to connect to your Pleroma instance with `ssh username@server -p $PORT`
+
+### :gopher
+* `enabled`: Enables the gopher interface
+* `ip`: IP address to bind to
+* `port`: Port to bind to
+* `dstport`: Port advertised in urls (optional, defaults to `port`)
+
+
+## Authentication
+
+### :admin_token
+
+Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter or `x-admin-token` HTTP header. Example:
+
+```elixir
+config :pleroma, :admin_token, "somerandomtoken"
+```
+
+You can then do
+
+```shell
+curl "http://localhost:4000/api/v1/pleroma/admin/users/invites?admin_token=somerandomtoken"
+```
+
+or
+
+```shell
+curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/v1/pleroma/admin/users/invites"
+```
+
+Warning: it's discouraged to use this feature because of the associated security risk: static / rarely changed instance-wide token is much weaker compared to email-password pair of a real admin user; consider using HTTP Basic Auth or OAuth-based authentication instead.
+
+### :auth
+
+Authentication / authorization settings.
+
+* `auth_template`: authentication form template. By default it's `show.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/show.html.eex`.
+* `oauth_consumer_template`: OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex`.
+* `oauth_consumer_strategies`: the list of enabled OAuth consumer strategies; by default it's set by `OAUTH_CONSUMER_STRATEGIES` environment variable. Each entry in this space-delimited string should be of format `<strategy>` or `<strategy>:<dependency>` (e.g. `twitter` or `keycloak:ueberauth_keycloak_strategy` in case dependency is named differently than `ueberauth_<strategy>`).
+
+### Pleroma.Web.Auth.Authenticator
+
+* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator.
+* `Pleroma.Web.Auth.LDAPAuthenticator`: LDAP authentication.
+
+### :ldap
+
+Use LDAP for user authentication. When a user logs in to the Pleroma
+instance, the name and password will be verified by trying to authenticate
+(bind) to an LDAP server. If a user exists in the LDAP directory but there
+is no account with the same name yet on the Pleroma instance then a new
+Pleroma account will be created with the same name as the LDAP user name.
+
+* `enabled`: enables LDAP authentication
+* `host`: LDAP server hostname
+* `port`: LDAP port, e.g. 389 or 636
+* `ssl`: true to use SSL, usually implies the port 636
+* `sslopts`: additional SSL options
+* `tls`: true to start TLS, usually implies the port 389
+* `tlsopts`: additional TLS options
+* `base`: LDAP base, e.g. "dc=example,dc=com"
+* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
+
+Note, if your LDAP server is an Active Directory server the correct value is commonly `uid: "cn"`, but if you use an
+OpenLDAP server the value may be `uid: "uid"`.
+
+### :oauth2 (Pleroma as OAuth 2.0 provider settings)
+
+OAuth 2.0 provider settings:
+
+* `token_expires_in` - The lifetime in seconds of the access token.
+* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token.
+* `clean_expired_tokens` - Enable a background job to clean expired oauth tokens. Defaults to `false`.
+
+OAuth 2.0 provider and related endpoints:
+
+* `POST /api/v1/apps` creates client app basing on provided params.
+* `GET/POST /oauth/authorize` renders/submits authorization form.
+* `POST /oauth/token` creates/renews OAuth token.
+* `POST /oauth/revoke` revokes provided OAuth token.
+* `GET /api/v1/accounts/verify_credentials` (with proper `Authorization` header or `access_token` URI param) returns user info on requester (with `acct` field containing local nickname and `fqn` field containing fully-qualified nickname which could generally be used as email stub for OAuth software that demands email field in identity endpoint response, like Peertube).
+
+### OAuth consumer mode
+
+OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.).
+Implementation is based on Ueberauth; see the list of [available strategies](https://github.com/ueberauth/ueberauth/wiki/List-of-Strategies).
+
+!!! note
+ Each strategy is shipped as a separate dependency; in order to get the strategies, run `OAUTH_CONSUMER_STRATEGIES="..." mix deps.get`, e.g. `OAUTH_CONSUMER_STRATEGIES="twitter facebook google microsoft" mix deps.get`. The server should also be started with `OAUTH_CONSUMER_STRATEGIES="..." mix phx.server` in case you enable any strategies.
+
+!!! note
+ Each strategy requires separate setup (on external provider side and Pleroma side). Below are the guidelines on setting up most popular strategies.
+
+!!! note
+ Make sure that `"SameSite=Lax"` is set in `extra_cookie_attrs` when you have this feature enabled. OAuth consumer mode will not work with `"SameSite=Strict"`
+
+* For Twitter, [register an app](https://developer.twitter.com/en/apps), configure callback URL to https://<your_host>/oauth/twitter/callback
+
+* For Facebook, [register an app](https://developers.facebook.com/apps), configure callback URL to https://<your_host>/oauth/facebook/callback, enable Facebook Login service at https://developers.facebook.com/apps/<app_id>/fb-login/settings/
+
+* For Google, [register an app](https://console.developers.google.com), configure callback URL to https://<your_host>/oauth/google/callback
+
+* For Microsoft, [register an app](https://portal.azure.com), configure callback URL to https://<your_host>/oauth/microsoft/callback
+
+Once the app is configured on external OAuth provider side, add app's credentials and strategy-specific settings (if any — e.g. see Microsoft below) to `config/prod.secret.exs`,
+per strategy's documentation (e.g. [ueberauth_twitter](https://github.com/ueberauth/ueberauth_twitter)). Example config basing on environment variables:
+
+```elixir
+# Twitter
+config :ueberauth, Ueberauth.Strategy.Twitter.OAuth,
+ consumer_key: System.get_env("TWITTER_CONSUMER_KEY"),
+ consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET")
+
+# Facebook
+config :ueberauth, Ueberauth.Strategy.Facebook.OAuth,
+ client_id: System.get_env("FACEBOOK_APP_ID"),
+ client_secret: System.get_env("FACEBOOK_APP_SECRET"),
+ redirect_uri: System.get_env("FACEBOOK_REDIRECT_URI")
+
+# Google
+config :ueberauth, Ueberauth.Strategy.Google.OAuth,
+ client_id: System.get_env("GOOGLE_CLIENT_ID"),
+ client_secret: System.get_env("GOOGLE_CLIENT_SECRET"),
+ redirect_uri: System.get_env("GOOGLE_REDIRECT_URI")
+
+# Microsoft
+config :ueberauth, Ueberauth.Strategy.Microsoft.OAuth,
+ client_id: System.get_env("MICROSOFT_CLIENT_ID"),
+ client_secret: System.get_env("MICROSOFT_CLIENT_SECRET")
+
+config :ueberauth, Ueberauth,
+ providers: [
+ microsoft: {Ueberauth.Strategy.Microsoft, [callback_params: []]}
+ ]
+
+# Keycloak
+# Note: make sure to add `keycloak:ueberauth_keycloak_strategy` entry to `OAUTH_CONSUMER_STRATEGIES` environment variable
+keycloak_url = "https://publicly-reachable-keycloak-instance.org:8080"
+
+config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
+ client_id: System.get_env("KEYCLOAK_CLIENT_ID"),
+ client_secret: System.get_env("KEYCLOAK_CLIENT_SECRET"),
+ site: keycloak_url,
+ authorize_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/auth",
+ token_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/token",
+ userinfo_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/userinfo",
+ token_method: :post
+
+config :ueberauth, Ueberauth,
+ providers: [
+ keycloak: {Ueberauth.Strategy.Keycloak, [uid_field: :email]}
+ ]
+```
+
+## Link parsing
+
+### :uri_schemes
+* `valid_schemes`: List of the scheme part that is considered valid to be an URL.
+
+### Pleroma.Formatter
+
+Configuration for Pleroma's link formatter which parses mentions, hashtags, and URLs.
+
+* `class` - specify the class to be added to the generated link (default: `false`)
+* `rel` - specify the rel attribute (default: `ugc`)
+* `new_window` - adds `target="_blank"` attribute (default: `false`)
+* `truncate` - Set to a number to truncate URLs longer then the number. Truncated URLs will end in `...` (default: `false`)
+* `strip_prefix` - Strip the scheme prefix (default: `false`)
+* `extra` - link URLs with rarely used schemes (magnet, ipfs, irc, etc.) (default: `true`)
+* `validate_tld` - Set to false to disable TLD validation for URLs/emails. Can be set to :no_scheme to validate TLDs only for urls without a scheme (e.g `example.com` will be validated, but `http://example.loki` won't) (default: `:no_scheme`)
+
+Example:
+
+```elixir
+config :pleroma, Pleroma.Formatter,
+ class: false,
+ rel: "ugc",
+ new_window: false,
+ truncate: false,
+ strip_prefix: false,
+ extra: true,
+ validate_tld: :no_scheme
+```
+
+## Custom Runtime Modules (`:modules`)
+
+* `runtime_dir`: A path to custom Elixir modules (such as MRF policies).
+
+## :configurable_from_database
+
+Boolean, enables/disables in-database configuration. Read [Transfering the config to/from the database](../administration/CLI_tasks/config.md) for more information.
+
+## :database_config_whitelist
+
+List of valid configuration sections which are allowed to be configured from the
+database. Settings stored in the database before the whitelist is configured are
+still applied, so it is suggested to only use the whitelist on instances that
+have not migrated the config to the database.
+
+Example:
+```elixir
+config :pleroma, :database_config_whitelist, [
+ {:pleroma, :instance},
+ {:pleroma, Pleroma.Web.Metadata},
+ {:auto_linker}
+]
+```
+
+### Multi-factor authentication - :two_factor_authentication
+* `totp` - a list containing TOTP configuration
+ - `digits` - Determines the length of a one-time pass-code in characters. Defaults to 6 characters.
+ - `period` - a period for which the TOTP code will be valid in seconds. Defaults to 30 seconds.
+* `backup_codes` - a list containing backup codes configuration
+ - `number` - number of backup codes to generate.
+ - `length` - backup code length. Defaults to 16 characters.
+
+## Restrict entities access for unauthenticated users
+
+### :restrict_unauthenticated
+
+Restrict access for unauthenticated users to timelines (public and federated), user profiles and statuses.
+
+* `timelines`: public and federated timelines
+ * `local`: public timeline
+ * `federated`: federated timeline (includes public timeline)
+* `profiles`: user profiles
+ * `local`
+ * `remote`
+* `activities`: statuses
+ * `local`
+ * `remote`
+
+Note: when `:instance, :public` is set to `false`, all `:restrict_unauthenticated` items be effectively set to `true` by default. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`.
+
+Note: setting `restrict_unauthenticated/timelines/local` to `true` has no practical sense if `restrict_unauthenticated/timelines/federated` is set to `false` (since local public activities will still be delivered to unauthenticated users as part of federated timeline).
+
+## Pleroma.Web.ApiSpec.CastAndValidate
+
+* `:strict` a boolean, enables strict input validation (useful in development, not recommended in production). Defaults to `false`.
+
+## :instances_favicons
+
+Control favicons for instances.
+
+* `enabled`: Allow/disallow displaying and getting instances favicons
+
+## Pleroma.User.Backup
+
+!!! note
+ Requires enabled email
+
+* `:purge_after_days` an integer, remove backup achives after N days.
+* `:limit_days` an integer, limit user to export not more often than once per N days.
+* `:dir` a string with a path to backup temporary directory or `nil` to let Pleroma choose temporary directory in the following order:
+ 1. the directory named by the TMPDIR environment variable
+ 2. the directory named by the TEMP environment variable
+ 3. the directory named by the TMP environment variable
+ 4. C:\TMP on Windows or /tmp on Unix-like operating systems
+ 5. as a last resort, the current working directory
+
+## Frontend management
+
+Frontends in Pleroma are swappable - you can specify which one to use here.
+
+You can set a frontends for the key `primary` and `admin` and the options of `name` and `ref`. This will then make Pleroma serve the frontend from a folder constructed by concatenating the instance static path, `frontends` and the name and ref.
+
+The key `primary` refers to the frontend that will be served by default for general requests. The key `admin` refers to the frontend that will be served at the `/pleroma/admin` path.
+
+If you don't set anything here, the bundled frontends will be used.
+
+Example:
+
+```
+config :pleroma, :frontends,
+ primary: %{
+ "name" => "pleroma",
+ "ref" => "stable"
+ },
+ admin: %{
+ "name" => "admin",
+ "ref" => "develop"
+ }
+```
+
+This would serve the frontend from the the folder at `$instance_static/frontends/pleroma/stable`. You have to copy the frontend into this folder yourself. You can choose the name and ref any way you like, but they will be used by mix tasks to automate installation in the future, the name referring to the project and the ref referring to a commit.
+
+## Ephemeral activities (Pleroma.Workers.PurgeExpiredActivity)
+
+Settings to enable and configure expiration for ephemeral activities
+
+* `:enabled` - enables ephemeral activities creation
+* `:min_lifetime` - minimum lifetime for ephemeral activities (in seconds). Default: 10 minutes.
+
+## ConcurrentLimiter
+
+Settings to restrict concurrently running jobs. Jobs which can be configured:
+
+* `Pleroma.Web.RichMedia.Helpers` - generating link previews of URLs in activities
+* `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy` - warming remote media cache via MediaProxyWarmingPolicy
+
+Each job has these settings:
+
+* `:max_running` - max concurrently runnings jobs
+* `:max_waiting` - max waiting jobs
diff --git a/docs/configuration/custom_emoji.md b/docs/configuration/custom_emoji.md
new file mode 100644
index 0000000..1648840
--- /dev/null
+++ b/docs/configuration/custom_emoji.md
@@ -0,0 +1,69 @@
+# Custom Emoji
+
+Before you add your own custom emoji, check if they are available in an existing pack.
+See `Mix.Tasks.Pleroma.Emoji` for information about emoji packs.
+
+To add custom emoji:
+
+* Create the `STATIC-DIR/emoji/` directory if it doesn't exist
+ (`STATIC-DIR` is configurable, `instance/static/` by default)
+* Create a directory with whatever name you want (custom is a good name to show the purpose of it).
+ This will create a local emoji pack.
+* Put your `.png` emoji files in that directory. In case of conflicts, you can create an `emoji.txt`
+ file in that directory and specify a custom shortcode using the following format:
+ `shortcode, file-path, tag1, tag2, etc`. One emoji per line. Note that if you do so,
+ you'll have to list all other emojis in the pack too.
+* Either restart pleroma or connect to the iex session pleroma's running and
+ run `Pleroma.Emoji.reload/0` in it.
+
+Example:
+
+image files (in `instance/static/emoji/custom`): `happy.png` and `sad.png`
+
+content of `emoji.txt`:
+```
+happy, /emoji/custom/happy.png, Tag1,Tag2
+sad, /emoji/custom/sad.png, Tag1
+foo, /emoji/custom/foo.png
+```
+
+The files should be PNG (APNG is okay with `.png` for `image/png` Content-type) and under 50kb for compatibility with mastodon.
+
+Default file extentions and locations for emojis are set in `config.exs`. To use different locations or file-extentions, add the `shortcode_globs` to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it. Note that not all fediverse-software will show emojis with other file extentions:
+```elixir
+config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png", "/emoji/custom/**/*.gif"]
+```
+
+## Emoji tags (groups)
+
+Default tags are set in `config.exs`. To set your own tags, copy the structure to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it.
+```elixir
+config :pleroma, :emoji,
+ shortcode_globs: ["/emoji/custom/**/*.png"],
+ groups: [
+ Finmoji: "/finmoji/128px/*-128.png",
+ Custom: ["/emoji/*.png", "/emoji/custom/*.png"]
+ ]
+```
+
+Order of the `groups` matters, so to override default tags just put your group on top of the list. E.g:
+```elixir
+config :pleroma, :emoji,
+ shortcode_globs: ["/emoji/custom/**/*.png"],
+ groups: [
+ "Finmoji special": "/finmoji/128px/a_trusted_friend-128.png", # special file
+ "Cirno": "/emoji/custom/cirno*.png", # png files in /emoji/custom/ which start with `cirno`
+ "Special group": "/emoji/custom/special_folder/*.png", # png files in /emoji/custom/special_folder/
+ "Another group": "/emoji/custom/special_folder/*/.png", # png files in /emoji/custom/special_folder/ subfolders
+ Finmoji: "/finmoji/128px/*-128.png",
+ Custom: ["/emoji/*.png", "/emoji/custom/*.png"]
+ ]
+```
+
+Priority of tags assigns in emoji.txt and custom.txt:
+
+`tag in file > special group setting in config.exs > default setting in config.exs`
+
+Priority for globs:
+
+`special group setting in config.exs > default setting in config.exs`
diff --git a/docs/configuration/hardening.md b/docs/configuration/hardening.md
new file mode 100644
index 0000000..d3bfc4e
--- /dev/null
+++ b/docs/configuration/hardening.md
@@ -0,0 +1,103 @@
+# Hardening your instance
+Here are some suggestions which improve the security of parts of your Pleroma instance.
+
+## Configuration file
+
+These changes should go into `prod.secret.exs` or `dev.secret.exs`, depending on your `MIX_ENV` value.
+
+### `http`
+
+> Recommended value: `[ip: {127, 0, 0, 1}]`
+
+This sets the Pleroma application server to only listen to the localhost interface. This way, you can only reach your server over the Internet by going through the reverse proxy. By default, Pleroma listens on all interfaces.
+
+### `secure_cookie_flag`
+
+> Recommended value: `true`
+
+This sets the `secure` flag on Pleroma’s session cookie. This makes sure, that the cookie is only accepted over encrypted HTTPs connections. This implicitly renames the cookie from `pleroma_key` to `__Host-pleroma-key` which enforces some restrictions. (see [cookie prefixes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Cookie_prefixes))
+
+### `:http_security`
+
+> Recommended value: `true`
+
+This will send additional HTTP security headers to the clients, including:
+
+* `X-XSS-Protection: "1; mode=block"`
+* `X-Permitted-Cross-Domain-Policies: "none"`
+* `X-Frame-Options: "DENY"`
+* `X-Content-Type-Options: "nosniff"`
+* `X-Download-Options: "noopen"`
+
+A content security policy (CSP) will also be set:
+
+```csp
+content-security-policy:
+ default-src 'none';
+ base-uri 'self';
+ frame-ancestors 'none';
+ img-src 'self' data: blob: https:;
+ media-src 'self' https:;
+ style-src 'self' 'unsafe-inline';
+ font-src 'self';
+ script-src 'self';
+ connect-src 'self' wss://example.tld;
+ manifest-src 'self';
+ upgrade-insecure-requests;
+```
+
+#### `sts`
+
+> Recommended value: `true`
+
+An additional “Strict transport security” header will be sent with the configured `sts_max_age` parameter. This tells the browser, that the domain should only be accessed over a secure HTTPs connection.
+
+#### `ct_max_age`
+
+An additional “Expect-CT” header will be sent with the configured `ct_max_age` parameter. This enforces the use of TLS certificates that are published in the certificate transparency log. (see [Expect-CT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT))
+
+#### `referrer_policy`
+
+> Recommended value: `same-origin`
+
+If you click on a link, your browser’s request to the other site will include from where it is coming from. The “Referrer policy” header tells the browser how and if it should send this information. (see [Referrer policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy))
+
+## systemd
+
+A systemd unit example is provided at `installation/pleroma.service`.
+
+### PrivateTmp
+
+> Recommended value: `true`
+
+Use private `/tmp` and `/var/tmp` folders inside a new file system namespace, which are discarded after the process stops.
+
+### ProtectHome
+
+> Recommended value: `true`
+
+The `/home`, `/root`, and `/run/user` folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to `false`.
+
+### ProtectSystem
+
+> Recommended value: `full`
+
+Mount `/usr`, `/boot`, and `/etc` as read-only for processes invoked by this service.
+
+### PrivateDevices
+
+> Recommended value: `true`
+
+Sets up a new `/dev` mount for the process and only adds API pseudo devices like `/dev/null`, `/dev/zero` or `/dev/random` but not physical devices. This may not work on devices like the Raspberry Pi, where you need to set this to `false`.
+
+### NoNewPrivileges
+
+> Recommended value: `true`
+
+Ensures that the service process and all its children can never gain new privileges through `execve()`.
+
+### CapabilityBoundingSet
+
+> Recommended value: `~CAP_SYS_ADMIN`
+
+Drops the sysadmin capability from the daemon.
diff --git a/docs/configuration/how_to_serve_another_domain_for_webfinger.md b/docs/configuration/how_to_serve_another_domain_for_webfinger.md
new file mode 100644
index 0000000..5ae3e79
--- /dev/null
+++ b/docs/configuration/how_to_serve_another_domain_for_webfinger.md
@@ -0,0 +1,62 @@
+# How to use a different domain name for Pleroma and the users it serves
+
+Pleroma users are primarily identified by a `user@example.org` handle, and you might want this identifier to be the same as your email or jabber account, for instance.
+However, in this case, you are almost certainly serving some web content on `https://example.org` already, and you might want to use another domain (say `pleroma.example.org`) for Pleroma itself.
+
+Pleroma supports that, but it might be tricky to set up, and any error might prevent you from federating with other instances.
+
+*If you are already running Pleroma on `example.org`, it is no longer possible to move it to `pleroma.example.org`.*
+
+## Account identifiers
+
+It is important to understand that for federation purposes, a user in Pleroma has two unique identifiers associated:
+
+- A webfinger `acct:` URI, used for discovery and as a verifiable global name for the user across Pleroma instances. In our example, our account's acct: URI is `acct:user@example.org`
+- An author/actor URI, used in every other aspect of federation. This is the way in which users are identified in ActivityPub, the underlying protocol used for federation with other Pleroma instances.
+In our case, it is `https://pleroma.example.org/users/user`.
+
+Both account identifiers are unique and required for Pleroma. An important risk if you set up your Pleroma instance incorrectly is to create two users (with different acct: URIs) with conflicting author/actor URIs.
+
+## WebFinger
+
+As said earlier, each Pleroma user has an `acct`: URI, which is used for discovery and authentication. When you add @user@example.org, a webfinger query is performed. This is done in two steps:
+
+1. Querying `https://example.org/.well-known/host-meta` (where the domain of the URL matches the domain part of the `acct`: URI) to get information on how to perform the query.
+This file will indeed contain a URL template of the form `https://example.org/.well-known/webfinger?resource={uri}` that will be used in the second step.
+2. Fill the returned template with the `acct`: URI to be queried and perform the query: `https://example.org/.well-known/webfinger?resource=acct:user@example.org`
+
+## Configuring your Pleroma instance
+
+**_DO NOT ATTEMPT TO CONFIGURE YOUR INSTANCE THIS WAY IF YOU DID NOT UNDERSTAND THE ABOVE_**
+
+### Configuring Pleroma
+
+Pleroma has a two configuration settings to enable using different domains for your users and Pleroma itself. `host` in `Pleroma.Web.Endpoint` and `domain` in `Pleroma.Web.WebFinger`. When the latter is not set, it defaults to the value of `host`.
+
+*Be extra careful when configuring your Pleroma instance, as changing `host` may cause remote instances to register different accounts with the same author/actor URI, which will result in federation issues!*
+
+```elixir
+config :pleroma, Pleroma.Web.Endpoint,
+ url: [host: "pleroma.example.org"]
+
+config :pleroma, Pleroma.Web.WebFinger, domain: "example.org"
+```
+
+- `domain` - is the domain for which your Pleroma instance has authority, it's the domain used in `acct:` URI. In our example, `domain` would be set to `example.org`. This is used in WebFinger account ids, which are the canonical account identifier in some other fediverse software like Mastodon. **If you change `domain`, the accounts on your server will be shown as different accounts in those software**.
+- `host` - is the domain used for any URL generated for your instance, including the author/actor URL's. In our case, that would be `pleroma.example.org`. This is used in AP ids, which are the canonical account identifier in Pleroma and some other fediverse software. **You should not change this after you have set up the instance**.
+
+### Configuring WebFinger domain
+
+Now, you have Pleroma running at `https://pleroma.example.org` as well as a website at `https://example.org`. If you recall how webfinger queries work, the first step is to query `https://example.org/.well-known/host-meta`, which will contain an URL template.
+
+Therefore, the easiest way to configure `example.org` is to redirect `/.well-known/host-meta` to `pleroma.example.org`.
+
+With nginx, it would be as simple as adding:
+
+```nginx
+location = /.well-known/host-meta {
+ return 301 https://pleroma.example.org$request_uri;
+}
+```
+
+in example.org's server block.
diff --git a/docs/configuration/howto_database_config.md b/docs/configuration/howto_database_config.md
new file mode 100644
index 0000000..e5af909
--- /dev/null
+++ b/docs/configuration/howto_database_config.md
@@ -0,0 +1,155 @@
+# How to activate Pleroma in-database configuration
+## Explanation
+
+The configuration of Pleroma has traditionally been managed with a config file, e.g. `config/prod.secret.exs`. This method requires a restart of the application for any configuration changes to take effect. We have made it possible to control most settings in the AdminFE interface after running a migration script.
+
+## Migration to database config
+
+1. Run the mix task to migrate to the database.
+
+ **Source:**
+
+ ```
+ $ mix pleroma.config migrate_to_db
+ ```
+
+ or
+
+ **OTP:**
+
+ *Note: OTP users need Pleroma to be running for `pleroma_ctl` commands to work*
+
+ ```
+ $ ./bin/pleroma_ctl config migrate_to_db
+ ```
+
+ ```
+ Migrating settings from file: /home/pleroma/config/dev.secret.exs
+
+ Settings for key instance migrated.
+ Settings for group :pleroma migrated.
+ ```
+
+2. It is recommended to backup your config file now.
+
+ ```
+ cp config/dev.secret.exs config/dev.secret.exs.orig
+ ```
+
+3. Edit your Pleroma config to enable database configuration:
+
+ ```
+ config :pleroma, configurable_from_database: true
+ ```
+
+4. ⚠️ **THIS IS NOT REQUIRED** ⚠️
+
+ Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres (Repo) and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
+
+ Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
+
+ A non-exhaustive list of settings that are only possible in the config file include the following:
+
+ * config :pleroma, Pleroma.Web.Endpoint
+ * config :pleroma, Pleroma.Repo
+ * config :pleroma, configurable\_from\_database
+ * config :pleroma, :database, rum_enabled
+ * config :pleroma, :connections_pool
+
+ Here is an example of a server config stripped down after migration:
+
+ ```
+ import Config
+
+ config :pleroma, Pleroma.Web.Endpoint,
+ url: [host: "cool.pleroma.site", scheme: "https", port: 443]
+
+ config :pleroma, Pleroma.Repo,
+ adapter: Ecto.Adapters.Postgres,
+ username: "pleroma",
+ password: "MySecretPassword",
+ database: "pleroma_prod",
+ hostname: "localhost"
+
+ config :pleroma, configurable_from_database: true
+ ```
+
+5. Restart your instance and you can now access the Settings tab in AdminFE.
+
+
+## Reverting back from database config
+
+1. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
+
+ **Source:**
+
+ ```
+ $ mix pleroma.config migrate_from_db
+ ```
+
+ or
+
+ **OTP:**
+
+ ```
+ $ ./bin/pleroma_ctl config migrate_from_db
+ ```
+
+ ```
+ 10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
+ SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
+
+ 10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
+ SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
+ Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
+ ```
+
+2. Remove `config :pleroma, configurable_from_database: true` from your config. The in-database configuration still exists, but it will not be used. Future migrations will erase the database config before importing your config file again.
+
+3. Restart your instance.
+
+## Debugging
+
+### Clearing database config
+You can clear the database config with the following command:
+
+ **Source:**
+
+ ```
+ $ mix pleroma.config reset
+ ```
+
+ or
+
+ **OTP:**
+
+ ```
+ $ ./bin/pleroma_ctl config reset
+ ```
+
+Additionally, every time you migrate the configuration to the database the config table is automatically truncated to ensure a clean migration.
+
+### Manually removing a setting
+If you encounter a situation where the server cannot run properly because of an invalid setting in the database and this is preventing you from accessing AdminFE, you can manually remove the offending setting if you know which one it is.
+
+e.g., here is an example showing a the removal of the `config :pleroma, :instance` settings:
+
+ **Source:**
+
+ ```
+ $ mix pleroma.config delete pleroma instance
+ Are you sure you want to continue? [n] y
+ config :pleroma, :instance deleted from the ConfigDB.
+ ```
+
+ or
+
+ **OTP:**
+
+ ```
+ $ ./bin/pleroma_ctl config delete pleroma instance
+ Are you sure you want to continue? [n] y
+ config :pleroma, :instance deleted from the ConfigDB.
+ ```
+
+Now the `config :pleroma, :instance` settings have been removed from the database.
diff --git a/docs/configuration/howto_ejabberd.md b/docs/configuration/howto_ejabberd.md
new file mode 100644
index 0000000..520a0ac
--- /dev/null
+++ b/docs/configuration/howto_ejabberd.md
@@ -0,0 +1,136 @@
+# Configuring Ejabberd (XMPP Server) to use Pleroma for authentication
+
+If you want to give your Pleroma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
+
+In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully.
+
+Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `PLEROMA_HOST` and `PLEROMA_PORT`, if necessary.
+
+```bash
+cp pleroma_ejabberd_auth.py /etc/ejabberd/pleroma_ejabberd_auth.py
+chown ejabberd /etc/ejabberd/pleroma_ejabberd_auth.py
+chmod 700 /etc/ejabberd/pleroma_ejabberd_auth.py
+```
+
+Set external auth params in ejabberd.yaml file:
+
+```bash
+auth_method: [external]
+extauth_program: "python3 /etc/ejabberd/pleroma_ejabberd_auth.py"
+extauth_instances: 3
+auth_use_cache: false
+```
+
+Restart / reload your ejabberd service.
+
+After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials.
+
+
+```python
+import sys
+import struct
+import http.client
+from base64 import b64encode
+import logging
+
+
+PLEROMA_HOST = "127.0.0.1"
+PLEROMA_PORT = "4000"
+AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials"
+USER_ENDPOINT = "/api/v1/accounts"
+LOGFILE = "/var/log/ejabberd/pleroma_auth.log"
+
+logging.basicConfig(filename=LOGFILE, level=logging.INFO)
+
+
+# Pleroma functions
+def create_connection():
+ return http.client.HTTPConnection(PLEROMA_HOST, PLEROMA_PORT)
+
+
+def verify_credentials(user: str, password: str) -> bool:
+ user_pass_b64 = b64encode("{}:{}".format(
+ user, password).encode('utf-8')).decode("ascii")
+ params = {}
+ headers = {
+ "Authorization": "Basic {}".format(user_pass_b64)
+ }
+
+ try:
+ conn = create_connection()
+ conn.request("GET", AUTH_ENDPOINT, params, headers)
+
+ response = conn.getresponse()
+ if response.status == 200:
+ return True
+
+ return False
+ except Exception as e:
+ logging.info("Can not connect: %s", str(e))
+ return False
+
+
+def does_user_exist(user: str) -> bool:
+ conn = create_connection()
+ conn.request("GET", "{}/{}".format(USER_ENDPOINT, user))
+
+ response = conn.getresponse()
+ if response.status == 200:
+ return True
+
+ return False
+
+
+def auth(username: str, server: str, password: str) -> bool:
+ return verify_credentials(username, password)
+
+
+def isuser(username, server):
+ return does_user_exist(username)
+
+
+def read():
+ (pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8'))
+ pkt = sys.stdin.read(pkt_size)
+ cmd = pkt.split(':')[0]
+ if cmd == 'auth':
+ username, server, password = pkt.split(':', 3)[1:]
+ write(auth(username, server, password))
+ elif cmd == 'isuser':
+ username, server = pkt.split(':', 2)[1:]
+ write(isuser(username, server))
+ elif cmd == 'setpass':
+ # u, s, p = pkt.split(':', 3)[1:]
+ write(False)
+ elif cmd == 'tryregister':
+ # u, s, p = pkt.split(':', 3)[1:]
+ write(False)
+ elif cmd == 'removeuser':
+ # u, s = pkt.split(':', 2)[1:]
+ write(False)
+ elif cmd == 'removeuser3':
+ # u, s, p = pkt.split(':', 3)[1:]
+ write(False)
+ else:
+ write(False)
+
+
+def write(result):
+ if result:
+ sys.stdout.write('\x00\x02\x00\x01')
+ else:
+ sys.stdout.write('\x00\x02\x00\x00')
+ sys.stdout.flush()
+
+
+if __name__ == "__main__":
+ logging.info("Starting pleroma ejabberd auth daemon...")
+ while True:
+ try:
+ read()
+ except Exception as e:
+ logging.info(
+ "Error while processing data from ejabberd %s", str(e))
+ pass
+
+``` \ No newline at end of file
diff --git a/docs/configuration/howto_mediaproxy.md b/docs/configuration/howto_mediaproxy.md
new file mode 100644
index 0000000..16c40c5
--- /dev/null
+++ b/docs/configuration/howto_mediaproxy.md
@@ -0,0 +1,34 @@
+# How to activate mediaproxy
+## Explanation
+
+Without the `mediaproxy` function, Pleroma doesn't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
+With the `mediaproxy` function you can use nginx to cache this content, so users can access it faster, because it's loaded from your server.
+
+## Activate it
+
+* Edit your nginx config and add the following location:
+```
+location /proxy {
+ proxy_cache pleroma_media_cache;
+ proxy_cache_lock on;
+ proxy_pass http://localhost:4000;
+}
+```
+Also add the following on top of the configuration, outside of the `server` block:
+```
+proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
+```
+If you came here from one of the installation guides, take a look at the example configuration `/installation/pleroma.nginx`, where this part is already included.
+
+* Append the following to your `prod.secret.exs` or `dev.secret.exs` (depends on which mode your instance is running):
+```
+config :pleroma, :media_proxy,
+ enabled: true,
+ proxy_opts: [
+ redirect_on_failure: true
+ ]
+ #base_url: "https://cache.pleroma.social"
+```
+If you want to use a subdomain to serve the files, uncomment `base_url`, change the url and add a comma after `true` in the previous line.
+
+* Restart nginx and Pleroma
diff --git a/docs/configuration/howto_mongooseim.md b/docs/configuration/howto_mongooseim.md
new file mode 100644
index 0000000..a33e590
--- /dev/null
+++ b/docs/configuration/howto_mongooseim.md
@@ -0,0 +1,10 @@
+# Configuring MongooseIM (XMPP Server) to use Pleroma for authentication
+
+If you want to give your Pleroma users an XMPP (chat) account, you can configure [MongooseIM](https://github.com/esl/MongooseIM) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
+
+In general, you just have to follow the configuration described at [https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/](https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/) and do these changes to your mongooseim.cfg.
+
+1. Set the auth_method to `{auth_method, http}`.
+2. Add the http auth pool like this: `{http, global, auth, [{workers, 50}], [{server, "https://yourpleromainstance.com"}]}`
+
+Restart your MongooseIM server, your users should now be able to connect with their Pleroma credentials.
diff --git a/docs/configuration/howto_proxy.md b/docs/configuration/howto_proxy.md
new file mode 100644
index 0000000..10a6352
--- /dev/null
+++ b/docs/configuration/howto_proxy.md
@@ -0,0 +1,12 @@
+# How to configure upstream proxy for federation
+If you want to proxify all http requests (e.g. for TOR) that pleroma makes to an upstream proxy server, edit you config file (`dev.secret.exs` or `prod.secret.exs`) and add the following:
+
+```
+config :pleroma, :http,
+ proxy_url: "127.0.0.1:8123"
+```
+
+The other way to do it, for example, with Tor you would most likely add something like this:
+```
+config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}
+```
diff --git a/docs/configuration/howto_search_cjk.md b/docs/configuration/howto_search_cjk.md
new file mode 100644
index 0000000..a73b10d
--- /dev/null
+++ b/docs/configuration/howto_search_cjk.md
@@ -0,0 +1,42 @@
+# How to enable text search for Chinese, Japanese and Korean
+
+Pleroma's full text search feature is powered by PostgreSQL's native [text search](https://www.postgresql.org/docs/current/textsearch.html), it works well out of box for most of languages, but needs extra configurations for some asian languages like Chinese, Japanese and Korean (CJK).
+
+
+## Setup and test the new search config
+
+In most cases, you would need an extension installed to support parsing CJK text. Here are a few extensions you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
+
+ * [a generic n-gram parser](https://github.com/huangjimmy/pg_cjk_parser) supports Simplifed/Traditional Chinese, Japanese, and Korean
+ * [a Korean parser](https://github.com/i0seph/textsearch_ko) based on mecab
+ * [a Japanese parser](https://www.amris.co.jp/tsja/index.html) based on mecab
+ * [zhparser](https://github.com/amutu/zhparser/) is a PostgreSQL extension base on the Simple Chinese Word Segmentation(SCWS)
+ * [another Chinese parser](https://github.com/jaiminpan/pg_jieba) based on Jieba Chinese Word Segmentation
+
+Once you have the new search config , make sure you test it with the `pleroma` user in PostgreSQL (change `YOUR.CONFIG` to your real configuration name)
+```
+SELECT ts_debug('YOUR.CONFIG', '安装和配置Nginx, ElixirとErlangをインストールします');
+```
+Check output of the query, and see if it matches your expectation.
+
+
+## Update text search config and index in database
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl database set_text_search_config YOUR.CONFIG
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.database set_text_search_config YOUR.CONFIG
+ ```
+
+Note: index update may take a while, and it can be done while the instance is up and running, so you may restart db connection as soon as you see `Recreate index` in task output.
+
+## Restart database connection
+Since some changes above will only apply with a new database connection, you will have to restart either Pleroma or PostgreSQL process, or use `pg_terminate_backend` SQL command without restarting either.
+
+Now the search results of statuses should be much more friendly for your language of choice, the results for searching users and tags were not changed, as the default parsing/matching should work for most cases.
diff --git a/docs/configuration/howto_set_richmedia_cache_ttl_based_on_image.md b/docs/configuration/howto_set_richmedia_cache_ttl_based_on_image.md
new file mode 100644
index 0000000..bfee5a9
--- /dev/null
+++ b/docs/configuration/howto_set_richmedia_cache_ttl_based_on_image.md
@@ -0,0 +1,33 @@
+# How to set rich media cache ttl based on image ttl
+## Explanation
+
+Richmedia are cached without the ttl but the rich media may have image which can expire, like aws signed url.
+In such cases the old image url (expired) is returned from the media cache.
+
+So to avoid such situation we can define a module that will set ttl based on image.
+The module must adopt behaviour `Pleroma.Web.RichMedia.Parser.TTL`
+
+### Example
+
+```exs
+defmodule MyModule do
+ @behaviour Pleroma.Web.RichMedia.Parser.TTL
+
+ @impl Pleroma.Web.RichMedia.Parser.TTL
+ def ttl(data, url) do
+ image_url = Map.get(data, :image)
+ # do some parsing in the url and get the ttl of the image
+ # return ttl is unix time
+ parse_ttl_from_url(image_url)
+ end
+end
+```
+
+And update the config
+
+```exs
+config :pleroma, :rich_media,
+ ttl_setters: [Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl, MyModule]
+```
+
+> For reference there is a parser for AWS signed URL `Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl`, it's enabled by default.
diff --git a/docs/configuration/howto_theming_your_instance.md b/docs/configuration/howto_theming_your_instance.md
new file mode 100644
index 0000000..cfa00f5
--- /dev/null
+++ b/docs/configuration/howto_theming_your_instance.md
@@ -0,0 +1,74 @@
+# Theming your instance
+
+To add a custom theme to your instance, you'll first need to get a custom theme, upload it to the server, make it available to the instance and eventually you can set it as default.
+
+## Getting a custom theme
+
+### Create your own theme
+
+* You can create your own theme using the Pleroma FE by going to settings (gear on the top right) and choose the Theme tab. Here you have the options to create a personal theme.
+* To download your theme, you can do Save preset
+* If you want to upload a theme to customise it further, you can upload it using Load preset
+
+This will only save the theme for you personally. To make it available to the whole instance, you'll need to upload it to the server.
+
+### Get an existing theme
+
+* You can download a theme from another instance by going to that instance, go to settings and make sure you have the theme selected that you want. Then you can do Save preset to download it.
+* You can also find and download custom themes at <https://plthemes.vulpes.one/>
+
+## Adding the custom theme to the instance
+
+### Upload the theme to the server
+
+Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `priv/static/static/styles.json`.
+
+Example of `styles.json` where we add our own `my-awesome-theme.json`
+```json
+{
+ "pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
+ "pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
+ "classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
+ "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
+ "ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
+ "monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
+
+ "redmond-xx": "/static/themes/redmond-xx.json",
+ "redmond-xx-se": "/static/themes/redmond-xx-se.json",
+ "redmond-xxi": "/static/themes/redmond-xxi.json",
+ "breezy-dark": "/static/themes/breezy-dark.json",
+ "breezy-light": "/static/themes/breezy-light.json",
+ "mammal": "/static/themes/mammal.json",
+ "my-awesome-theme": "/static/themes/my-awesome-theme.json"
+}
+```
+
+Now you'll already be able to select the theme in Pleroma FE from the drop-down. You don't need to restart Pleroma because we only changed static served files. You may need to refresh the page in your browser. You'll notice however that the theme doesn't have a name, it's just an empty entry in the drop-down.
+
+### Give the theme a name
+
+When you open one of the themes that ship with Pleroma, you'll notice that the json has a `"name"` key. Add a key-value pair to your theme where the key name is `"name"` and the value the name you want to give your theme. After this you can refresh te page in your browser and the name should be visible in the drop-down.
+
+Example of `my-awesome-theme.json` where we add the name "My Awesome Theme"
+```json
+{
+ "_pleroma_theme_version": 2,
+ "name": "My Awesome Theme",
+ "theme": {}
+}
+```
+
+### Set as default theme
+
+Now we can set the new theme as default in the [Pleroma FE configuration](../../../frontend/CONFIGURATION).
+
+Example of adding the new theme in the back-end config files
+```elixir
+config :pleroma, :frontend_configurations,
+ pleroma_fe: %{
+ theme: "my-awesome-theme"
+ }
+```
+
+If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.
+
diff --git a/docs/configuration/i2p.md b/docs/configuration/i2p.md
new file mode 100644
index 0000000..8c5207d
--- /dev/null
+++ b/docs/configuration/i2p.md
@@ -0,0 +1,196 @@
+# I2P Federation and Accessability
+
+This guide is going to focus on the Pleroma federation aspect. The actual installation is neatly explained in the official documentation, and more likely to remain up-to-date.
+It might be added to this guide if there will be a need for that.
+
+We're going to use I2PD for its lightweightness over the official client.
+Follow the documentation according to your distro: https://i2pd.readthedocs.io/en/latest/user-guide/install/#installing
+
+How to run it: https://i2pd.readthedocs.io/en/latest/user-guide/run/
+
+## I2P Federation
+
+There are 2 ways to go about this.
+One using the config, and one using external software (fedproxy). The external software works better so far.
+
+### Using the Config
+
+**Warning:** So far, everytime I followed this way of federating using I2P, the rest of my federation stopped working. I'm leaving this here in case it will help with making it work.
+
+Assuming you're running in prod, cd to your Pleroma folder and append the following to `config/prod.secret.exs`:
+```
+config :pleroma, :http, proxy_url: {:socks5, :localhost, 4447}
+```
+And then run the following:
+```
+su pleroma
+MIX_ENV=prod mix deps.get
+MIX_ENV=prod mix ecto.migrate
+exit
+```
+You can restart I2PD here and finish if you don't wish to make your instance viewable or accessible over I2P.
+```
+systemctl stop i2pd.service --no-block
+systemctl start i2pd.service
+```
+*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
+
+You can change the socks proxy port in `/etc/i2pd/i2pd.conf`.
+
+### Using Fedproxy
+
+Fedproxy passes through clearnet requests direct to where they are going. It doesn't force anything over Tor.
+
+To use [fedproxy](https://github.com/majestrate/fedproxy) you'll need to install Golang.
+```
+apt install golang
+```
+Use a different user than pleroma or root. Run the following to add the Gopath to your ~/.bashrc.
+```
+echo "export GOPATH=/home/ren/.go" >> ~/.bashrc
+```
+Restart that bash session (you can exit and log back in).
+Run the following to get fedproxy.
+```
+go get -u github.com/majestrate/fedproxy$
+cp $(GOPATH)/bin/fedproxy /usr/local/bin/fedproxy
+```
+And then the following to start it for I2P only.
+```
+fedproxy 127.0.0.1:2000 127.0.0.1:4447
+```
+If you want to also use it for Tor, add `127.0.0.1:9050` to that command.
+You'll also need to modify your Pleroma config.
+
+Assuming you're running in prod, cd to your Pleroma folder and append the following to `config/prod.secret.exs`:
+```
+config :pleroma, :http, proxy_url: {:socks5, :localhost, 2000}
+```
+And then run the following:
+```
+su pleroma
+MIX_ENV=prod mix deps.get
+MIX_ENV=prod mix ecto.migrate
+exit
+```
+You can restart I2PD here and finish if you don't wish to make your instance viewable or accessible over I2P.
+
+```
+systemctl stop i2pd.service --no-block
+systemctl start i2pd.service
+```
+*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
+
+You can change the socks proxy port in `/etc/i2pd/i2pd.conf`.
+
+## I2P Instance Access
+
+Make your instance accessible using I2P.
+
+Add the following to your I2PD config `/etc/i2pd/tunnels.conf`:
+```
+[pleroma]
+type = http
+host = 127.0.0.1
+port = 14447
+keys = pleroma.dat
+```
+Restart I2PD:
+```
+systemctl stop i2pd.service --no-block
+systemctl start i2pd.service
+```
+*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
+
+Now you'll have to find your address.
+To do that you can download and use I2PD tools.[^1]
+Or you'll need to access your web-console on localhost:7070.
+If you don't have a GUI, you'll have to SSH tunnel into it like this:
+`ssh -L 7070:127.0.0.1:7070 user@ip -p port`.
+Now you can access it at localhost:7070.
+Go to I2P tunnels page. Look for Server tunnels and you will see an address that ends with `.b32.i2p` next to "pleroma".
+This is your site's address.
+
+### I2P-only Instance
+
+If creating an I2P-only instance, open `config/prod.secret.exs` and under "config :pleroma, Pleroma.Web.Endpoint," edit "https" and "port: 443" to the following:
+```
+ url: [host: "i2paddress", scheme: "http", port: 80],
+```
+In addition to that, replace the existing nginx config's contents with the example below.
+
+### Existing Instance (Clearnet Instance)
+
+If not an I2P-only instance, add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
+
+And for both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
+Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
+```
+config :pleroma, :http_security,
+ enabled: false
+```
+
+Use this as the Nginx config:
+```
+proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
+# The above already exists in a clearnet instance's config.
+# If not, add it.
+
+server {
+ listen 127.0.0.1:14447;
+ server_name youri2paddress;
+
+ # Comment to enable logs
+ access_log /dev/null;
+ error_log /dev/null;
+
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_buffers 16 8k;
+ gzip_http_version 1.1;
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
+
+ client_max_body_size 16m;
+
+ location / {
+
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Permitted-Cross-Domain-Policies none;
+ add_header X-Frame-Options DENY;
+ add_header X-Content-Type-Options nosniff;
+ add_header Referrer-Policy same-origin;
+ add_header X-Download-Options noopen;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $http_host;
+
+ proxy_pass http://localhost:4000;
+
+ client_max_body_size 16m;
+ }
+
+ location /proxy {
+ proxy_cache pleroma_media_cache;
+ proxy_cache_lock on;
+ proxy_ignore_client_abort on;
+ proxy_pass http://localhost:4000;
+ }
+}
+```
+reload Nginx:
+```
+systemctl stop i2pd.service --no-block
+systemctl start i2pd.service
+```
+*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
+
+You should now be able to both access your instance using I2P and federate with other I2P instances!
+
+[^1]: [I2PD tools](https://github.com/purplei2p/i2pd-tools) to print information about a router info file or an I2P private key, generate an I2P private key, and generate vanity addresses.
+
+### Possible Issues
+
+Will be added when encountered.
diff --git a/docs/configuration/mrf.md b/docs/configuration/mrf.md
new file mode 100644
index 0000000..a31c26b
--- /dev/null
+++ b/docs/configuration/mrf.md
@@ -0,0 +1,158 @@
+# Message Rewrite Facility
+
+The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
+
+Possible uses include:
+
+* marking incoming messages with media from a given account or instance as sensitive
+* rejecting messages from a specific instance
+* rejecting reports (flags) from a specific instance
+* removing/unlisting messages from the public timelines
+* removing media from messages
+* sending only public messages to a specific instance
+
+The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
+
+It is possible to use multiple, active MRF policies at the same time.
+
+## Quarantine Instances
+
+You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance.
+
+If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes.
+
+```elixir
+config :pleroma, :instance,
+ [...]
+ quarantined_instances: ["instance.example", "other.example"]
+```
+
+## Using `SimplePolicy`
+
+`SimplePolicy` is capable of handling most common admin tasks.
+
+To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
+
+```elixir
+config :pleroma, :mrf,
+ [...]
+ policies: Pleroma.Web.ActivityPub.MRF.SimplePolicy
+```
+
+Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
+
+* `reject`: Servers in this group will have their messages rejected.
+* `accept`: If not empty, only messages from these instances will be accepted (whitelist federation).
+* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
+* `media_removal`: Servers in this group will have media stripped from incoming messages.
+* `avatar_removal`: Avatars from these servers will be stripped from incoming messages.
+* `banner_removal`: Banner images from these servers will be stripped from incoming messages.
+* `report_removal`: Servers in this group will have their reports (flags) rejected.
+* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
+* `reject_deletes`: Deletion requests will be rejected from these servers.
+
+Servers should be configured as lists.
+
+### Example
+
+This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`. We also give a reason why the moderation was done:
+
+```elixir
+config :pleroma, :mrf,
+ policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
+
+config :pleroma, :mrf_simple,
+ media_removal: [{"illegalporn.biz", "Media can contain illegal contant"}],
+ media_nsfw: [{"porn.biz", "unmarked nsfw media"}, {"porn.business", "A lot of unmarked nsfw media"}],
+ reject: [{"spam.com", "They keep spamming our users"}],
+ federated_timeline_removal: [{"spam.university", "Annoying low-quality posts who otherwise fill up TWKN"}],
+ report_removal: [{"whiny.whiner", "Keep spamming us with irrelevant reports"}]
+```
+
+### Use with Care
+
+The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
+
+## Writing your own MRF Policy
+
+As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `policies` config setting.
+
+For example, here is a sample policy module which rewrites all messages to "new message content":
+
+```elixir
+defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
+ @moduledoc "MRF policy which rewrites all Notes to have 'new message content'."
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
+
+ # Catch messages which contain Note objects with actual data to filter.
+ # Capture the object as `object`, the message content as `content` and the
+ # message itself as `message`.
+ @impl true
+ def filter(
+ %{"type" => "Create", "object" => %{"type" => "Note", "content" => content} = object} =
+ message
+ )
+ when is_binary(content) do
+ # Subject / CW is stored as summary instead of `name` like other AS2 objects
+ # because of Mastodon doing it that way.
+ summary = object["summary"]
+
+ # Message edits go here.
+ content = "new message content"
+
+ # Assemble the mutated object.
+ object =
+ object
+ |> Map.put("content", content)
+ |> Map.put("summary", summary)
+
+ # Assemble the mutated message.
+ message = Map.put(message, "object", object)
+ {:ok, message}
+ end
+
+ # Let all other messages through without modifying them.
+ @impl true
+ def filter(message), do: {:ok, message}
+
+ @impl true
+ def describe do
+ {:ok, %{mrf_sample: %{content: "new message content"}}}
+ end
+end
+```
+
+If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
+
+```elixir
+config :pleroma, :mrf,
+ policies: [
+ Pleroma.Web.ActivityPub.MRF.SimplePolicy,
+ Pleroma.Web.ActivityPub.MRF.RewritePolicy
+ ]
+```
+
+Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request.
+
+### MRF policies descriptions
+
+If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns a map with a specific structure. See existing MRF's like `lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex` for examples. Note that more complex inputs, like tuples or maps, may need extra changes in the adminFE and just adding it to `config_description/0` may not be enough to get these inputs working from the adminFE.
+
+Example:
+
+```elixir
+%{
+ key: :mrf_activity_expiration,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
+ label: "MRF Activity Expiration Policy",
+ description: "Adds automatic expiration to all local activities",
+ children: [
+ %{
+ key: :days,
+ type: :integer,
+ description: "Default global expiration time for all local activities (in days)",
+ suggestions: [90, 365]
+ }
+ ]
+ }
+```
diff --git a/docs/configuration/onion_federation.md b/docs/configuration/onion_federation.md
new file mode 100644
index 0000000..3767321
--- /dev/null
+++ b/docs/configuration/onion_federation.md
@@ -0,0 +1,159 @@
+# Easy Onion Federation (Tor)
+Tor can free people from the necessity of a domain, in addition to helping protect their privacy. As Pleroma's goal is to empower the people and let as many as possible host an instance with as little resources as possible, the ability to host an instance with a small, cheap computer like a RaspberryPi along with Tor, would be a great way to achieve that.
+In addition, federating with such instances will also help furthering that goal.
+
+This is a guide to show you how it can be easily done.
+
+This guide assumes you already got Pleroma working, and that it's running on the default port 4000.
+Currently only has an Nginx example.
+
+To install Tor on Debian / Ubuntu:
+```
+apt -yq install tor
+```
+If using an old server version (older than Debian Stretch or Ubuntu 18.04), install from backports or PPA.
+I recommend using a newer server version instead.
+
+To have the newest, V3 onion addresses (which I recommend) in Debian, install Tor from backports.
+If you do not have backports, uncomment the stretch-backports links at the end of `/etc/apt/sources.list`.
+Then install:
+```
+apt update
+apt -t stretch-backports -yq install tor
+```
+**WARNING:** Onion instances not using a Tor version supporting V3 addresses will not be able to federate with you.
+
+Create the hidden service for your Pleroma instance in `/etc/tor/torrc`:
+```
+HiddenServiceDir /var/lib/tor/pleroma_hidden_service/
+HiddenServicePort 80 127.0.0.1:8099
+HiddenServiceVersion 3 # Remove if Tor version is below 0.3 ( tor --version )
+```
+Restart Tor to generate an adress:
+```
+systemctl restart tor@default.service
+```
+Get the address:
+```
+cat /var/lib/tor/pleroma_hidden_service/hostname
+```
+
+# Federation
+
+Next, edit your Pleroma config.
+If running in prod, cd to your Pleroma directory, edit `config/prod.secret.exs`
+and append this line:
+```
+config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}
+```
+In your Pleroma directory, assuming you're running prod,
+run the following:
+```
+su pleroma
+MIX_ENV=prod mix deps.get
+MIX_ENV=prod mix ecto.migrate
+exit
+```
+restart Pleroma (if using systemd):
+```
+systemctl restart pleroma
+```
+
+# Tor Instance Access
+
+Make your instance accessible using Tor.
+
+## Tor-only Instance
+If creating a Tor-only instance, open `config/prod.secret.exs` and under "config :pleroma, Pleroma.Web.Endpoint," edit "https" and "port: 443" to the following:
+```
+ url: [host: "onionaddress", scheme: "http", port: 80],
+```
+In addition to that, replace the existing nginx config's contents with the example below.
+
+## Existing Instance (Clearnet Instance)
+If not a Tor-only instance,
+add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
+
+---
+For both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
+Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
+```
+config :pleroma, :http_security,
+ enabled: false
+```
+
+Use this as the Nginx config:
+```
+proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
+# The above already exists in a clearnet instance's config.
+# If not, add it.
+
+server {
+ listen 127.0.0.1:8099;
+ server_name youronionaddress;
+
+ # Comment to enable logs
+ access_log /dev/null;
+ error_log /dev/null;
+
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_buffers 16 8k;
+ gzip_http_version 1.1;
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
+
+ client_max_body_size 16m;
+
+ location / {
+
+ add_header X-XSS-Protection "1; mode=block";
+ add_header X-Permitted-Cross-Domain-Policies none;
+ add_header X-Frame-Options DENY;
+ add_header X-Content-Type-Options nosniff;
+ add_header Referrer-Policy same-origin;
+ add_header X-Download-Options noopen;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $http_host;
+
+ proxy_pass http://localhost:4000;
+
+ client_max_body_size 16m;
+ }
+
+ location /proxy {
+ proxy_cache pleroma_media_cache;
+ proxy_cache_lock on;
+ proxy_ignore_client_abort on;
+ proxy_pass http://localhost:4000;
+ }
+}
+```
+reload Nginx:
+```
+systemctl reload nginx
+```
+
+You should now be able to both access your instance using Tor and federate with other Tor instances!
+
+---
+
+### Possible Issues
+
+* In Debian, make sure your hidden service folder `/var/lib/tor/pleroma_hidden_service/` and its contents, has debian-tor as both owner and group by using
+```
+ls -la /var/lib/tor/
+```
+If it's not, run:
+```
+chown -R debian-tor:debian-tor /var/lib/tor/pleroma_hidden_service/
+```
+* Make sure *only* the owner has *only* read and write permissions.
+If not, run:
+```
+chmod -R 600 /var/lib/tor/pleroma_hidden_service/
+```
+* If you have trouble logging in to the Mastodon Frontend when using Tor, use the Tor Browser Bundle.
diff --git a/docs/configuration/optimizing_beam.md b/docs/configuration/optimizing_beam.md
new file mode 100644
index 0000000..e336bd3
--- /dev/null
+++ b/docs/configuration/optimizing_beam.md
@@ -0,0 +1,66 @@
+# Optimizing the BEAM
+
+Pleroma is built upon the Erlang/OTP VM known as BEAM. The BEAM VM is highly optimized for latency, but this has drawbacks in environments without dedicated hardware. One of the tricks used by the BEAM VM is [busy waiting](https://en.wikipedia.org/wiki/Busy_waiting). This allows the application to pretend to be busy working so the OS kernel does not pause the application process and switch to another process waiting for the CPU to execute its workload. It does this by spinning for a period of time which inflates the apparent CPU usage of the application so it is immediately ready to execute another task. This can be observed with utilities like **top(1)** which will show consistently high CPU usage for the process. Switching between procesess is a rather expensive operation and also clears CPU caches further affecting latency and performance. The goal of busy waiting is to avoid this penalty.
+
+This strategy is very successful in making a performant and responsive application, but is not desirable on Virtual Machines or hardware with few CPU cores. Pleroma instances are often deployed on the same server as the required PostgreSQL database which can lead to situations where the Pleroma application is holding the CPU in a busy-wait loop and as a result the database cannot process requests in a timely manner. The fewer CPUs available, the more this problem is exacerbated. The latency is further amplified by the OS being installed on a Virtual Machine as the Hypervisor uses CPU time-slicing to pause the entire OS and switch between other tasks.
+
+More adventurous admins can be creative with CPU affinity (e.g., *taskset* for Linux and *cpuset* on FreeBSD) to pin processes to specific CPUs and eliminate much of this contention. The most important advice is to run as few processes as possible on your server to achieve the best performance. Even idle background processes can occasionally create [software interrupts](https://en.wikipedia.org/wiki/Interrupt) and take attention away from the executing process creating latency spikes and invalidation of the CPU caches as they must be cleared when switching between processes for security.
+
+Please only change these settings if you are experiencing issues or really know what you are doing. In general, there's no need to change these settings.
+
+## VPS Provider Recommendations
+
+### Good
+
+* Hetzner Cloud
+
+### Bad
+
+* AWS (known to use burst scheduling)
+
+
+## Example configurations
+
+Tuning the BEAM requires you provide a config file normally called [vm.args](http://erlang.org/doc/man/erl.html#emulator-flags). If you are using systemd to manage the service you can modify the unit file as such:
+
+`ExecStart=/usr/bin/elixir --erl '-args_file /opt/pleroma/config/vm.args' -S /usr/bin/mix phx.server`
+
+Check your OS documentation to adopt a similar strategy on other platforms.
+
+### Virtual Machine and/or few CPU cores
+
+Disable the busy-waiting. This should generally only be done if you're on a platform that does burst scheduling, like AWS.
+
+**vm.args:**
+
+```
++sbwt none
++sbwtdcpu none
++sbwtdio none
+```
+
+### Dedicated Hardware
+
+Enable more busy waiting, increase the internal maximum limit of BEAM processes and ports. You can use this if you run on dedicated hardware, but it is not necessary.
+
+**vm.args:**
+
+```
++P 16777216
++Q 16777216
++K true
++A 128
++sbt db
++sbwt very_long
++swt very_low
++sub true
++Mulmbcs 32767
++Mumbcgs 1
++Musmbcs 2047
+```
+
+## Additional Reading
+
+* [WhatsApp: Scaling to Millions of Simultaneous Connections](https://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf)
+* [Preemptive Scheduling and Spinlocks](https://www.uio.no/studier/emner/matnat/ifi/nedlagte-emner/INF3150/h03/annet/slides/preemptive.pdf)
+* [The Curious Case of BEAM CPU Usage](https://stressgrid.com/blog/beam_cpu_usage/)
diff --git a/docs/configuration/postgresql.md b/docs/configuration/postgresql.md
new file mode 100644
index 0000000..e251eb8
--- /dev/null
+++ b/docs/configuration/postgresql.md
@@ -0,0 +1,48 @@
+# Optimizing PostgreSQL performance
+
+Pleroma performance is largely dependent on performance of the underlying database. Better performance can be achieved by adjusting a few settings.
+
+## PGTune
+
+[PgTune](https://pgtune.leopard.in.ua) can be used to get recommended settings. Be sure to set "Number of Connections" to 20, otherwise it might produce settings hurtful to database performance. It is also recommended to not use "Network Storage" option.
+
+## Disable generic query plans
+
+When PostgreSQL receives a query, it decides on a strategy for searching the requested data, this is called a query plan. The query planner has two modes: generic and custom. Generic makes a plan for all queries of the same shape, ignoring the parameters, which is then cached and reused. Custom, on the contrary, generates a unique query plan based on query parameters.
+
+By default PostgreSQL has an algorithm to decide which mode is more efficient for particular query, however this algorithm has been observed to be wrong on some of the queries Pleroma sends, leading to serious performance loss. Therefore, it is recommended to disable generic mode.
+
+
+Pleroma already avoids generic query plans by default, however the method it uses is not the most efficient because it needs to be compatible with all supported PostgreSQL versions. For PostgreSQL 12 and higher additional performance can be gained by adding the following to Pleroma configuration:
+```elixir
+config :pleroma, Pleroma.Repo,
+ prepare: :named,
+ parameters: [
+ plan_cache_mode: "force_custom_plan"
+ ]
+```
+
+A more detailed explaination of the issue can be found at <https://blog.soykaf.com/post/postgresql-elixir-troubles/>.
+
+## Example configurations
+
+Here are some configuration suggestions for PostgreSQL 10+.
+
+### 1GB RAM, 1 CPU
+```
+shared_buffers = 256MB
+effective_cache_size = 768MB
+maintenance_work_mem = 64MB
+work_mem = 13107kB
+```
+
+### 2GB RAM, 2 CPU
+```
+shared_buffers = 512MB
+effective_cache_size = 1536MB
+maintenance_work_mem = 128MB
+work_mem = 26214kB
+max_worker_processes = 2
+max_parallel_workers_per_gather = 1
+max_parallel_workers = 2
+```
diff --git a/docs/configuration/static_dir.md b/docs/configuration/static_dir.md
new file mode 100644
index 0000000..a294bb6
--- /dev/null
+++ b/docs/configuration/static_dir.md
@@ -0,0 +1,95 @@
+# Static Directory
+
+Static frontend files are shipped with pleroma. If you want to overwrite or update these without problems during upgrades, you can write your custom versions to the static directory.
+
+You can find the location of the static directory in the [configuration](../cheatsheet/#instance).
+
+=== "OTP"
+
+ ```elixir
+ config :pleroma, :instance,
+ static_dir: "/var/lib/pleroma/static/"
+ ```
+
+=== "From Source"
+
+ ```elixir
+ config :pleroma, :instance,
+ static_dir: "instance/static/"
+ ```
+
+Alternatively, you can overwrite this value in your configuration to use a different static instance directory.
+
+This document is written using `$static_dir` as the value of the `config :pleroma, :instance, static_dir` setting.
+
+If you use a From Source installation and want to manage your custom files in the git repository, you can remove the `instance/` entry from `.gitignore`.
+
+## robots.txt
+
+There's a mix tasks to [generate a new robot.txt](../../administration/CLI_tasks/robots_txt/).
+
+For more complex things, you can write your own robots.txt to `$static_dir/robots.txt`.
+
+E.g. if you want to block all crawlers except for [fediverse.network](https://fediverse.network/about) you can use
+
+```
+User-Agent: *
+Disallow: /
+
+User-Agent: crawler-us-il-1.fediverse.network
+Allow: /
+
+User-Agent: makhnovtchina.random.sh
+Allow: /
+```
+
+## Thumbnail
+
+Add `$static_dir/instance/thumbnail.jpeg` with your selfie or other neat picture. It will be available on `http://your-domain.tld/instance/thumbnail.jpeg` and can be used by external applications.
+
+## Instance-specific panel
+
+Create and Edit your file at `$static_dir/instance/panel.html`.
+
+## Background
+
+You can change the background of your Pleroma instance by uploading it to `$static_dir/`, and then changing `background` in [your configuration](../cheatsheet/#frontend_configurations) accordingly.
+
+E.g. if you put `$static_dir/images/background.jpg`
+
+```
+config :pleroma, :frontend_configurations,
+ pleroma_fe: %{
+ background: "/images/background.jpg"
+ }
+```
+
+## Logo
+
+!!! important
+ Note the extra `static` folder for the default logo.png location
+
+If you want to give a brand to your instance, You can change the logo of your instance by uploading it to the static directory `$static_dir/static/logo.png`.
+
+Alternatively, you can specify the path to your logo in [your configuration](../cheatsheet/#frontend_configurations).
+
+E.g. if you put `$static_dir/static/mylogo-file.png`
+
+```
+config :pleroma, :frontend_configurations,
+ pleroma_fe: %{
+ logo: "/static/mylogo-file.png"
+ }
+```
+
+## Terms of Service
+
+!!! important
+ Note the extra `static` folder for the terms-of-service.html
+
+Terms of Service will be shown to all users on the registration page. It's the best place where to write down the rules for your instance. You can modify the rules by adding and changing `$static_dir/static/terms-of-service.html`.
+
+
+## Styling rendered pages
+
+To overwrite the CSS stylesheet of the OAuth form and other static pages, you can upload your own CSS file to `instance/static/static.css`. This will completely replace the CSS used by those pages, so it might be a good idea to copy the one from `priv/static/instance/static.css` and make your changes.
diff --git a/docs/configuration/storing_remote_media.md b/docs/configuration/storing_remote_media.md
new file mode 100644
index 0000000..c01985d
--- /dev/null
+++ b/docs/configuration/storing_remote_media.md
@@ -0,0 +1,38 @@
+# Storing Remote Media
+
+Pleroma does not store remote/federated media by default. The best way to achieve this is to change Nginx to keep its reverse proxy cache
+for a year and to activate the `MediaProxyWarmingPolicy` MRF policy in Pleroma which will automatically fetch all media through the proxy
+as soon as the post is received by your instance.
+
+## Nginx
+
+```
+ proxy_cache_path /long/term/storage/path/pleroma-media-cache levels=1:2
+ keys_zone=pleroma_media_cache:10m inactive=1y use_temp_path=off;
+
+ location ~ ^/(media|proxy) {
+ proxy_cache pleroma_media_cache;
+ slice 1m;
+ proxy_cache_key $host$uri$is_args$args$slice_range;
+ proxy_set_header Range $slice_range;
+ proxy_http_version 1.1;
+ proxy_cache_valid 206 301 302 304 1h;
+ proxy_cache_valid 200 1y;
+ proxy_cache_use_stale error timeout invalid_header updating;
+ proxy_ignore_client_abort on;
+ proxy_buffering on;
+ chunked_transfer_encoding on;
+ proxy_ignore_headers Cache-Control Expires;
+ proxy_hide_header Cache-Control Expires;
+ proxy_pass http://127.0.0.1:4000;
+ }
+```
+
+## Pleroma
+
+Add to your `prod.secret.exs`:
+
+```
+config :pleroma, :mrf,
+ policies: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
+```
diff --git a/docs/development/API/admin_api.md b/docs/development/API/admin_api.md
new file mode 100644
index 0000000..f6e9f7d
--- /dev/null
+++ b/docs/development/API/admin_api.md
@@ -0,0 +1,1751 @@
+# Admin API
+
+Authentication is required and the user must be an admin.
+
+The `/api/v1/pleroma/admin/*` path is backwards compatible with `/api/pleroma/admin/*` (`/api/pleroma/admin/*` will be deprecated in the future).
+
+## `GET /api/v1/pleroma/admin/users`
+
+### List users
+
+- Query Params:
+ - *optional* `query`: **string** search term (e.g. nickname, domain, nickname@domain)
+ - *optional* `filters`: **string** comma-separated string of filters:
+ - `local`: only local users
+ - `external`: only external users
+ - `active`: only active users
+ - `need_approval`: only unapproved users
+ - `unconfirmed`: only unconfirmed users
+ - `deactivated`: only deactivated users
+ - `is_admin`: users with admin role
+ - `is_moderator`: users with moderator role
+ - *optional* `page`: **integer** page number
+ - *optional* `page_size`: **integer** number of users per page (default is `50`)
+ - *optional* `tags`: **[string]** tags list
+ - *optional* `actor_types`: **[string]** actor type list (`Person`, `Service`, `Application`)
+ - *optional* `name`: **string** user display name
+ - *optional* `email`: **string** user email
+- Example: `https://mypleroma.org/api/v1/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
+- Response:
+
+```json
+{
+ "page_size": integer,
+ "count": integer,
+ "users": [
+ {
+ "deactivated": bool,
+ "id": integer,
+ "nickname": string,
+ "roles": {
+ "admin": bool,
+ "moderator": bool
+ },
+ "local": bool,
+ "tags": array,
+ "avatar": string,
+ "display_name": string,
+ "confirmation_pending": bool,
+ "approval_pending": bool,
+ "registration_reason": string,
+ },
+ ...
+ ]
+}
+```
+
+## DEPRECATED `DELETE /api/v1/pleroma/admin/users`
+
+### Remove a user
+
+- Params:
+ - `nickname`
+- Response: User’s nickname
+
+## `DELETE /api/v1/pleroma/admin/users`
+
+### Remove a user
+
+- Params:
+ - `nicknames`
+- Response: Array of user nicknames
+
+### Create a user
+
+- Method: `POST`
+- Params:
+ `users`: [
+ {
+ `nickname`,
+ `email`,
+ `password`
+ }
+ ]
+- Response: User’s nickname
+
+## `POST /api/v1/pleroma/admin/users/follow`
+
+### Make a user follow another user
+
+- Params:
+ - `follower`: The nickname of the follower
+ - `followed`: The nickname of the followed
+- Response:
+ - "ok"
+
+## `POST /api/v1/pleroma/admin/users/unfollow`
+
+### Make a user unfollow another user
+
+- Params:
+ - `follower`: The nickname of the follower
+ - `followed`: The nickname of the followed
+- Response:
+ - "ok"
+
+## `PATCH /api/v1/pleroma/admin/users/:nickname/toggle_activation`
+
+### Toggle user activation
+
+- Params:
+ - `nickname`
+- Response: User’s object
+
+```json
+{
+ "deactivated": bool,
+ "id": integer,
+ "nickname": string
+}
+```
+
+## `PUT /api/v1/pleroma/admin/users/tag`
+
+### Tag a list of users
+
+- Params:
+ - `nicknames` (array)
+ - `tags` (array)
+
+## `DELETE /api/v1/pleroma/admin/users/tag`
+
+### Untag a list of users
+
+- Params:
+ - `nicknames` (array)
+ - `tags` (array)
+
+## `GET /api/v1/pleroma/admin/users/:nickname/permission_group`
+
+### Get user user permission groups membership
+
+- Params: none
+- Response:
+
+```json
+{
+ "is_moderator": bool,
+ "is_admin": bool
+}
+```
+
+## `GET /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
+Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist.
+
+### Get user user permission groups membership per permission group
+
+- Params: none
+- Response:
+
+```json
+{
+ "is_moderator": bool,
+ "is_admin": bool
+}
+```
+
+## DEPRECATED `POST /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
+### Add user to permission group
+
+- Params: none
+- Response:
+ - On failure: `{"error": "…"}`
+ - On success: JSON of the user
+
+## `POST /api/v1/pleroma/admin/users/permission_group/:permission_group`
+
+### Add users to permission group
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+ - On failure: `{"error": "…"}`
+ - On success: JSON of the user
+
+## DEPRECATED `DELETE /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
+## `DELETE /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
+### Remove user from permission group
+
+- Params: none
+- Response:
+ - On failure: `{"error": "…"}`
+ - On success: JSON of the user
+- Note: An admin cannot revoke their own admin status.
+
+## `DELETE /api/v1/pleroma/admin/users/permission_group/:permission_group`
+
+### Remove users from permission group
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+ - On failure: `{"error": "…"}`
+ - On success: JSON of the user
+- Note: An admin cannot revoke their own admin status.
+
+## `PATCH /api/v1/pleroma/admin/users/activate`
+
+### Activate user
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+ users: [
+ {
+ // user object
+ }
+ ]
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/deactivate`
+
+### Deactivate user
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+ users: [
+ {
+ // user object
+ }
+ ]
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/approve`
+
+### Approve user
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+ users: [
+ {
+ // user object
+ }
+ ]
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/suggest`
+
+### Suggest a user
+
+Adds the user(s) to follower recommendations.
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+ users: [
+ {
+ // user object
+ }
+ ]
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/unsuggest`
+
+### Unsuggest a user
+
+Removes the user(s) from follower recommendations.
+
+- Params:
+ - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+ users: [
+ {
+ // user object
+ }
+ ]
+}
+```
+
+## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
+
+### Retrive the details of a user
+
+- Params:
+ - `nickname` or `id`
+- Response:
+ - On failure: `Not found`
+ - On success: JSON of the user
+
+## `GET /api/v1/pleroma/admin/users/:nickname_or_id/statuses`
+
+### Retrive user's latest statuses
+
+- Params:
+ - `nickname` or `id`
+ - *optional* `page_size`: number of statuses to return (default is `20`)
+ - *optional* `godmode`: `true`/`false` – allows to see private statuses
+ - *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false)
+- Response:
+ - On failure: `Not found`
+ - On success: JSON, where:
+ - `total`: total count of the statuses for the user
+ - `activities`: list of the statuses for the user
+
+```json
+{
+ "total" : 1,
+ "activities": [
+ // activities list
+ ]
+}
+```
+
+## `GET /api/v1/pleroma/admin/instances/:instance/statuses`
+
+### Retrive instance's latest statuses
+
+- Params:
+ - `instance`: instance name
+ - *optional* `page_size`: number of statuses to return (default is `20`)
+ - *optional* `godmode`: `true`/`false` – allows to see private statuses
+ - *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false)
+- Response:
+ - On failure: `Not found`
+ - On success: JSON, where:
+ - `total`: total count of the statuses for the instance
+ - `activities`: list of the statuses for the instance
+
+```json
+{
+ "total" : 1,
+ "activities": [
+ // activities list
+ ]
+}
+```
+
+## `DELETE /api/v1/pleroma/admin/instances/:instance`
+
+### Delete all users and activities from a remote instance
+
+Note: this will trigger a job to remove instance content in the background.
+It may take some time.
+
+- Params:
+ - `instance`: remote instance host
+- Response:
+ - The `instance` name as a string
+
+```json
+"lain.com"
+```
+
+## `GET /api/v1/pleroma/admin/statuses`
+
+### Retrives all latest statuses
+
+- Params:
+ - *optional* `page_size`: number of statuses to return (default is `20`)
+ - *optional* `local_only`: excludes remote statuses
+ - *optional* `godmode`: `true`/`false` – allows to see private statuses
+ - *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false)
+- Response:
+ - On failure: `Not found`
+ - On success: JSON array of user's latest statuses
+
+## `GET /api/v1/pleroma/admin/relay`
+
+### List Relays
+
+Params: none
+Response:
+
+* On success: JSON array of relays
+
+```json
+[
+ {"actor": "https://example.com/relay", "followed_back": true},
+ {"actor": "https://example2.com/relay", "followed_back": false}
+]
+```
+
+## `POST /api/v1/pleroma/admin/relay`
+
+### Follow a Relay
+
+Params:
+
+* `relay_url`
+
+Response:
+
+* On success: relay json object
+
+```json
+{"actor": "https://example.com/relay", "followed_back": true}
+```
+
+## `DELETE /api/v1/pleroma/admin/relay`
+
+### Unfollow a Relay
+
+- Params:
+ - `relay_url`
+ - *optional* `force`: forcefully unfollow a relay even when the relay is not available. (default is `false`)
+
+Response:
+
+* On success: URL of the unfollowed relay
+
+```json
+{"https://example.com/relay"}
+```
+
+## `POST /api/v1/pleroma/admin/users/invite_token`
+
+### Create an account registration invite token
+
+- Params:
+ - *optional* `max_use` (integer)
+ - *optional* `expires_at` (date string e.g. "2019-04-07")
+- Response:
+
+```json
+{
+ "id": integer,
+ "token": string,
+ "used": boolean,
+ "expires_at": date,
+ "uses": integer,
+ "max_use": integer,
+ "invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
+}
+```
+
+## `GET /api/v1/pleroma/admin/users/invites`
+
+### Get a list of generated invites
+
+- Params: none
+- Response:
+
+```json
+{
+
+ "invites": [
+ {
+ "id": integer,
+ "token": string,
+ "used": boolean,
+ "expires_at": date,
+ "uses": integer,
+ "max_use": integer,
+ "invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
+ },
+ ...
+ ]
+}
+```
+
+## `POST /api/v1/pleroma/admin/users/revoke_invite`
+
+### Revoke invite by token
+
+- Params:
+ - `token`
+- Response:
+
+```json
+{
+ "id": integer,
+ "token": string,
+ "used": boolean,
+ "expires_at": date,
+ "uses": integer,
+ "max_use": integer,
+ "invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
+
+}
+```
+
+## `POST /api/v1/pleroma/admin/users/email_invite`
+
+### Sends registration invite via email
+
+- Params:
+ - `email`
+ - `name`, optional
+
+- Response:
+ - On success: `204`, empty response
+ - On failure:
+ - 400 Bad Request, JSON:
+
+ ```json
+ [
+ {
+ "error": "Appropriate error message here"
+ }
+ ]
+ ```
+
+## `GET /api/v1/pleroma/admin/users/:nickname/password_reset`
+
+### Get a password reset token for a given nickname
+
+
+- Params: none
+- Response:
+
+```json
+{
+ "token": "base64 reset token",
+ "link": "https://pleroma.social/api/v1/pleroma/password_reset/url-encoded-base64-token"
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/force_password_reset`
+
+### Force passord reset for a user with a given nickname
+
+- Params:
+ - `nicknames`
+- Response: none (code `204`)
+
+## PUT `/api/v1/pleroma/admin/users/disable_mfa`
+
+### Disable mfa for user's account.
+
+- Params:
+ - `nickname`
+- Response: User’s nickname
+
+## `GET /api/v1/pleroma/admin/users/:nickname/credentials`
+
+### Get the user's email, password, display and settings-related fields
+
+- Params:
+ - `nickname`
+
+- Response:
+
+```json
+{
+ "actor_type": "Person",
+ "allow_following_move": true,
+ "avatar": "https://pleroma.social/media/7e8e7508fd545ef580549b6881d80ec0ff2c81ed9ad37b9bdbbdf0e0d030159d.jpg",
+ "background": "https://pleroma.social/media/4de34c0bd10970d02cbdef8972bef0ebbf55f43cadc449554d4396156162fe9a.jpg",
+ "banner": "https://pleroma.social/media/8d92ba2bd244b613520abf557dd448adcd30f5587022813ee9dd068945986946.jpg",
+ "bio": "bio",
+ "default_scope": "public",
+ "discoverable": false,
+ "email": "user@example.com",
+ "fields": [
+ {
+ "name": "example",
+ "value": "<a href=\"https://example.com\" rel=\"ugc\">https://example.com</a>"
+ }
+ ],
+ "hide_favorites": false,
+ "hide_followers": false,
+ "hide_followers_count": false,
+ "hide_follows": false,
+ "hide_follows_count": false,
+ "id": "9oouHaEEUR54hls968",
+ "locked": true,
+ "name": "user",
+ "no_rich_text": true,
+ "pleroma_settings_store": {},
+ "raw_fields": [
+ {
+ "id": 1,
+ "name": "example",
+ "value": "https://example.com"
+ },
+ ],
+ "show_role": true,
+ "skip_thread_containment": false
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/users/:nickname/credentials`
+
+### Change the user's email, password, display and settings-related fields
+
+* Params:
+ * `email`
+ * `password`
+ * `name`
+ * `bio`
+ * `avatar`
+ * `locked`
+ * `no_rich_text`
+ * `default_scope`
+ * `banner`
+ * `hide_follows`
+ * `hide_followers`
+ * `hide_followers_count`
+ * `hide_follows_count`
+ * `hide_favorites`
+ * `allow_following_move`
+ * `background`
+ * `show_role`
+ * `skip_thread_containment`
+ * `fields`
+ * `is_discoverable`
+ * `actor_type`
+
+* Responses:
+
+Status: 200
+
+```json
+{"status": "success"}
+```
+
+Status: 400
+
+```json
+{"errors":
+ {"actor_type": "is invalid"},
+ {"email": "has invalid format"},
+ ...
+ }
+```
+
+Status: 404
+
+```json
+{"error": "Not found"}
+```
+
+## `GET /api/v1/pleroma/admin/reports`
+
+### Get a list of reports
+
+- Params:
+ - *optional* `state`: **string** the state of reports. Valid values are `open`, `closed` and `resolved`
+ - *optional* `limit`: **integer** the number of records to retrieve
+ - *optional* `page`: **integer** page number
+ - *optional* `page_size`: **integer** number of log entries per page (default is `50`)
+- Response:
+ - On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
+ - On success: JSON, returns a list of reports, where:
+ - `account`: the user who has been reported
+ - `actor`: the user who has sent the report
+ - `statuses`: list of statuses that have been included to the report
+
+```json
+{
+ "total" : 1,
+ "reports": [
+ {
+ "account": {
+ "acct": "user",
+ "avatar": "https://pleroma.example.org/images/avi.png",
+ "avatar_static": "https://pleroma.example.org/images/avi.png",
+ "bot": false,
+ "created_at": "2019-04-23T17:32:04.000Z",
+ "display_name": "User",
+ "emojis": [],
+ "fields": [],
+ "followers_count": 1,
+ "following_count": 1,
+ "header": "https://pleroma.example.org/images/banner.png",
+ "header_static": "https://pleroma.example.org/images/banner.png",
+ "id": "9i6dAJqSGSKMzLG2Lo",
+ "locked": false,
+ "note": "",
+ "pleroma": {
+ "confirmation_pending": false,
+ "hide_favorites": true,
+ "hide_followers": false,
+ "hide_follows": false,
+ "is_admin": false,
+ "is_moderator": false,
+ "relationship": {},
+ "tags": []
+ },
+ "source": {
+ "note": "",
+ "pleroma": {},
+ "sensitive": false
+ },
+ "tags": ["force_unlisted"],
+ "statuses_count": 3,
+ "url": "https://pleroma.example.org/users/user",
+ "username": "user"
+ },
+ "actor": {
+ "acct": "lain",
+ "avatar": "https://pleroma.example.org/images/avi.png",
+ "avatar_static": "https://pleroma.example.org/images/avi.png",
+ "bot": false,
+ "created_at": "2019-03-28T17:36:03.000Z",
+ "display_name": "Roger Braun",
+ "emojis": [],
+ "fields": [],
+ "followers_count": 1,
+ "following_count": 1,
+ "header": "https://pleroma.example.org/images/banner.png",
+ "header_static": "https://pleroma.example.org/images/banner.png",
+ "id": "9hEkA5JsvAdlSrocam",
+ "locked": false,
+ "note": "",
+ "pleroma": {
+ "confirmation_pending": false,
+ "hide_favorites": false,
+ "hide_followers": false,
+ "hide_follows": false,
+ "is_admin": false,
+ "is_moderator": false,
+ "relationship": {},
+ "tags": []
+ },
+ "source": {
+ "note": "",
+ "pleroma": {},
+ "sensitive": false
+ },
+ "tags": ["force_unlisted"],
+ "statuses_count": 1,
+ "url": "https://pleroma.example.org/users/lain",
+ "username": "lain"
+ },
+ "content": "Please delete it",
+ "created_at": "2019-04-29T19:48:15.000Z",
+ "id": "9iJGOv1j8hxuw19bcm",
+ "state": "open",
+ "statuses": [
+ {
+ "account": { ... },
+ "application": {
+ "name": "Web",
+ "website": null
+ },
+ "bookmarked": false,
+ "card": null,
+ "content": "<span class=\"h-card\"><a data-user=\"9hEkA5JsvAdlSrocam\" class=\"u-url mention\" href=\"https://pleroma.example.org/users/lain\">@<span>lain</span></a></span> click on my link <a href=\"https://www.google.com/\">https://www.google.com/</a>",
+ "created_at": "2019-04-23T19:15:47.000Z",
+ "emojis": [],
+ "favourited": false,
+ "favourites_count": 0,
+ "id": "9i6mQ9uVrrOmOime8m",
+ "in_reply_to_account_id": null,
+ "in_reply_to_id": null,
+ "language": null,
+ "media_attachments": [],
+ "mentions": [
+ {
+ "acct": "lain",
+ "id": "9hEkA5JsvAdlSrocam",
+ "url": "https://pleroma.example.org/users/lain",
+ "username": "lain"
+ },
+ {
+ "acct": "user",
+ "id": "9i6dAJqSGSKMzLG2Lo",
+ "url": "https://pleroma.example.org/users/user",
+ "username": "user"
+ }
+ ],
+ "muted": false,
+ "pinned": false,
+ "pleroma": {
+ "content": {
+ "text/plain": "@lain click on my link https://www.google.com/"
+ },
+ "conversation_id": 28,
+ "in_reply_to_account_acct": null,
+ "local": true,
+ "spoiler_text": {
+ "text/plain": ""
+ }
+ },
+ "reblog": null,
+ "reblogged": false,
+ "reblogs_count": 0,
+ "replies_count": 0,
+ "sensitive": false,
+ "spoiler_text": "",
+ "tags": [],
+ "uri": "https://pleroma.example.org/objects/8717b90f-8e09-4b58-97b0-e3305472b396",
+ "url": "https://pleroma.example.org/notice/9i6mQ9uVrrOmOime8m",
+ "visibility": "direct"
+ }
+ ]
+ }
+ ]
+}
+```
+
+## `GET /api/v1/pleroma/admin/grouped_reports`
+
+### Get a list of reports, grouped by status
+
+- Params: none
+- On success: JSON, returns a list of reports, where:
+ - `date`: date of the latest report
+ - `account`: the user who has been reported (see `/api/v1/pleroma/admin/reports` for reference)
+ - `status`: reported status (see `/api/v1/pleroma/admin/reports` for reference)
+ - `actors`: users who had reported this status (see `/api/v1/pleroma/admin/reports` for reference)
+ - `reports`: reports (see `/api/v1/pleroma/admin/reports` for reference)
+
+```json
+ "reports": [
+ {
+ "date": "2019-10-07T12:31:39.615149Z",
+ "account": { ... },
+ "status": { ... },
+ "actors": [{ ... }, { ... }],
+ "reports": [{ ... }]
+ }
+ ]
+```
+
+## `GET /api/v1/pleroma/admin/reports/:id`
+
+### Get an individual report
+
+- Params:
+ - `id`
+- Response:
+ - On failure:
+ - 403 Forbidden `{"error": "error_msg"}`
+ - 404 Not Found `"Not found"`
+ - On success: JSON, Report object (see above)
+
+## `PATCH /api/v1/pleroma/admin/reports`
+
+### Change the state of one or multiple reports
+
+- Params:
+
+```json
+ `reports`: [
+ {
+ `id`, // required, report id
+ `state` // required, the new state. Valid values are `open`, `closed` and `resolved`
+ },
+ ...
+ ]
+```
+
+- Response:
+ - On failure:
+ - 400 Bad Request, JSON:
+
+ ```json
+ [
+ {
+ `id`, // report id
+ `error` // error message
+ }
+ ]
+ ```
+
+ - On success: `204`, empty response
+
+## `POST /api/v1/pleroma/admin/reports/:id/notes`
+
+### Create report note
+
+- Params:
+ - `id`: required, report id
+ - `content`: required, the message
+- Response:
+ - On failure:
+ - 400 Bad Request `"Invalid parameters"` when `status` is missing
+ - On success: `204`, empty response
+
+## `DELETE /api/v1/pleroma/admin/reports/:report_id/notes/:id`
+
+### Delete report note
+
+- Params:
+ - `report_id`: required, report id
+ - `id`: required, note id
+- Response:
+ - On failure:
+ - 400 Bad Request `"Invalid parameters"` when `status` is missing
+ - On success: `204`, empty response
+
+## `GET /api/v1/pleroma/admin/statuses/:id`
+
+### Show status by id
+
+- Params:
+ - `id`: required, status id
+- Response:
+ - On failure:
+ - 404 Not Found `"Not Found"`
+ - On success: JSON, Mastodon Status entity
+
+## `PUT /api/v1/pleroma/admin/statuses/:id`
+
+### Change the scope of an individual reported status
+
+- Params:
+ - `id`
+ - `sensitive`: optional, valid values are `true` or `false`
+ - `visibility`: optional, valid values are `public`, `private` and `unlisted`
+- Response:
+ - On failure:
+ - 400 Bad Request `"Unsupported visibility"`
+ - 403 Forbidden `{"error": "error_msg"}`
+ - 404 Not Found `"Not found"`
+ - On success: JSON, Mastodon Status entity
+
+## `DELETE /api/v1/pleroma/admin/statuses/:id`
+
+### Delete an individual reported status
+
+- Params:
+ - `id`
+- Response:
+ - On failure:
+ - 403 Forbidden `{"error": "error_msg"}`
+ - 404 Not Found `"Not found"`
+ - On success: 200 OK `{}`
+
+## `GET /api/v1/pleroma/admin/restart`
+
+### Restarts pleroma application
+
+**Only works when configuration from database is enabled.**
+
+- Params: none
+- Response:
+ - On failure:
+ - 400 Bad Request `"To use this endpoint you need to enable configuration from database."`
+
+```json
+{}
+```
+
+## `GET /api/v1/pleroma/admin/need_reboot`
+
+### Returns the flag whether the pleroma should be restarted
+
+- Params: none
+- Response:
+ - `need_reboot` - boolean
+```json
+{
+ "need_reboot": false
+}
+```
+
+## `GET /api/v1/pleroma/admin/config`
+
+### Get list of merged default settings with saved in database.
+
+*If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.*
+
+**Only works when configuration from database is enabled.**
+
+- Params:
+ - `only_db`: true (*optional*, get only saved in database settings)
+- Response:
+ - On failure:
+ - 400 Bad Request `"To use this endpoint you need to enable configuration from database."`
+
+```json
+{
+ "configs": [
+ {
+ "group": ":pleroma",
+ "key": "Pleroma.Upload",
+ "value": []
+ }
+ ],
+ "need_reboot": true
+}
+```
+
+## `POST /api/v1/pleroma/admin/config`
+
+### Update config settings
+
+*If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.*
+
+**Only works when configuration from database is enabled.**
+
+Some modifications are necessary to save the config settings correctly:
+
+- strings which start with `Pleroma.`, `Phoenix.`, `Tesla.` or strings like `Oban`, `Ueberauth` will be converted to modules;
+```
+"Pleroma.Upload" -> Pleroma.Upload
+"Oban" -> Oban
+```
+- strings starting with `:` will be converted to atoms;
+```
+":pleroma" -> :pleroma
+```
+- objects with `tuple` key and array value will be converted to tuples;
+```
+{"tuple": ["string", "Pleroma.Upload", []]} -> {"string", Pleroma.Upload, []}
+```
+- arrays with *tuple objects* will be converted to keywords;
+```
+[{"tuple": [":key1", "value"]}, {"tuple": [":key2", "value"]}] -> [key1: "value", key2: "value"]
+```
+
+Most of the settings will be applied in `runtime`, this means that you don't need to restart the instance. But some settings are applied in `compile time` and require a reboot of the instance, such as:
+- all settings inside these keys:
+ - `:hackney_pools`
+ - `:connections_pool`
+ - `:pools`
+ - `:chat`
+- partially settings inside these keys:
+ - `:seconds_valid` in `Pleroma.Captcha`
+ - `:proxy_remote` in `Pleroma.Upload`
+ - `:upload_limit` in `:instance`
+
+- Params:
+ - `configs` - array of config objects
+ - config object params:
+ - `group` - string (**required**)
+ - `key` - string (**required**)
+ - `value` - string, [], {} or {"tuple": []} (**required**)
+ - `delete` - true (*optional*, if setting must be deleted)
+ - `subkeys` - array of strings (*optional*, only works when `delete=true` parameter is passed, otherwise will be ignored)
+
+*When a value have several nested settings, you can delete only some nested settings by passing a parameter `subkeys`, without deleting all settings by key.*
+```
+[subkey: val1, subkey2: val2, subkey3: val3] \\ initial value
+{"group": ":pleroma", "key": "some_key", "delete": true, "subkeys": [":subkey", ":subkey3"]} \\ passing json for deletion
+[subkey2: val2] \\ value after deletion
+```
+
+*Most of the settings can be partially updated through merge old values with new values, except settings value of which is list or is not keyword.*
+
+Example of setting without keyword in value:
+```elixir
+config :tesla, :adapter, Tesla.Adapter.Hackney
+```
+
+List of settings which support only full update by key:
+```elixir
+@full_key_update [
+ {:pleroma, :ecto_repos},
+ {:mime, :types},
+ {:cors_plug, [:max_age, :methods, :expose, :headers]},
+ {:auto_linker, :opts},
+ {:swarm, :node_blacklist},
+ {:logger, :backends}
+ ]
+```
+
+List of settings which support only full update by subkey:
+```elixir
+@full_subkey_update [
+ {:pleroma, :assets, :mascots},
+ {:pleroma, :emoji, :groups},
+ {:pleroma, :workers, :retries},
+ {:pleroma, :mrf_subchain, :match_actor},
+ {:pleroma, :mrf_keyword, :replace}
+ ]
+```
+
+*Settings without explicit key must be sent in separate config object params.*
+```elixir
+config :foo,
+ bar: :baz,
+ meta: [:data],
+ ...
+```
+```json
+{
+ "configs": [
+ {"group": ":foo", "key": ":bar", "value": ":baz"},
+ {"group": ":foo", "key": ":meta", "value": [":data"]},
+ ...
+ ]
+}
+```
+- Request:
+
+```json
+{
+ "configs": [
+ {
+ "group": ":pleroma",
+ "key": "Pleroma.Upload",
+ "value": [
+ {"tuple": [":uploader", "Pleroma.Uploaders.Local"]},
+ {"tuple": [":filters", ["Pleroma.Upload.Filter.Dedupe"]]},
+ {"tuple": [":link_name", true]},
+ {"tuple": [":proxy_remote", false]},
+ {"tuple": [":proxy_opts", [
+ {"tuple": [":redirect_on_failure", false]},
+ {"tuple": [":max_body_length", 1048576]},
+ {"tuple": [":http", [
+ {"tuple": [":follow_redirect", true]},
+ {"tuple": [":pool", ":upload"]},
+ ]]}
+ ]
+ ]},
+ {"tuple": [":dispatch", {
+ "tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []]
+ }]}
+ ]
+ }
+ ]
+}
+```
+
+- Response:
+ - On failure:
+ - 400 Bad Request `"To use this endpoint you need to enable configuration from database."`
+```json
+{
+ "configs": [
+ {
+ "group": ":pleroma",
+ "key": "Pleroma.Upload",
+ "value": [...]
+ }
+ ],
+ "need_reboot": true
+}
+```
+
+## ` GET /api/v1/pleroma/admin/config/descriptions`
+
+### Get JSON with config descriptions.
+Loads json generated from `config/descriptions.exs`.
+
+- Params: none
+- Response:
+
+```json
+[{
+ "group": ":pleroma", // string
+ "key": "ModuleName", // string
+ "type": "group", // string or list with possible values,
+ "description": "Upload general settings", // string
+ "children": [
+ {
+ "key": ":uploader", // string or module name `Pleroma.Upload`
+ "type": "module",
+ "description": "Module which will be used for uploads",
+ "suggestions": ["module1", "module2"]
+ },
+ {
+ "key": ":filters",
+ "type": ["list", "module"],
+ "description": "List of filter modules for uploads",
+ "suggestions": [
+ "module1", "module2", "module3"
+ ]
+ }
+ ]
+}]
+```
+
+## `GET /api/v1/pleroma/admin/moderation_log`
+
+### Get moderation log
+
+- Params:
+ - *optional* `page`: **integer** page number
+ - *optional* `page_size`: **integer** number of log entries per page (default is `50`)
+ - *optional* `start_date`: **datetime (ISO 8601)** filter logs by creation date, start from `start_date`. Accepts datetime in ISO 8601 format (YYYY-MM-DDThh:mm:ss), e.g. `2005-08-09T18:31:42`
+ - *optional* `end_date`: **datetime (ISO 8601)** filter logs by creation date, end by from `end_date`. Accepts datetime in ISO 8601 format (YYYY-MM-DDThh:mm:ss), e.g. 2005-08-09T18:31:42
+ - *optional* `user_id`: **integer** filter logs by actor's id
+ - *optional* `search`: **string** search logs by the log message
+- Response:
+
+```json
+[
+ {
+ "id": 1234,
+ "data": {
+ "actor": {
+ "id": 1,
+ "nickname": "lain"
+ },
+ "action": "relay_follow"
+ },
+ "time": 1502812026, // timestamp
+ "message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
+ }
+]
+```
+
+## `POST /api/v1/pleroma/admin/reload_emoji`
+
+### Reload the instance's custom emoji
+
+- Authentication: required
+- Params: None
+- Response: JSON, "ok" and 200 status
+
+## `PATCH /api/v1/pleroma/admin/users/confirm_email`
+
+### Confirm users' emails
+
+- Params:
+ - `nicknames`
+- Response: Array of user nicknames
+
+## `PATCH /api/v1/pleroma/admin/users/resend_confirmation_email`
+
+### Resend confirmation email
+
+- Params:
+ - `nicknames`
+- Response: Array of user nicknames
+
+## `GET /api/v1/pleroma/admin/stats`
+
+### Stats
+
+- Query Params:
+ - *optional* `instance`: **string** instance hostname (without protocol) to get stats for
+- Example: `https://mypleroma.org/api/v1/pleroma/admin/stats?instance=lain.com`
+
+- Response:
+
+```json
+{
+ "status_visibility": {
+ "direct": 739,
+ "private": 9,
+ "public": 17,
+ "unlisted": 14
+ }
+}
+```
+
+## `GET /api/v1/pleroma/admin/oauth_app`
+
+### List OAuth app
+
+- Params:
+ - *optional* `name`
+ - *optional* `client_id`
+ - *optional* `page`
+ - *optional* `page_size`
+ - *optional* `trusted`
+
+- Response:
+
+```json
+{
+ "apps": [
+ {
+ "id": 1,
+ "name": "App name",
+ "client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
+ "client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
+ "redirect_uri": "https://example.com/oauth-callback",
+ "website": "https://example.com",
+ "trusted": true
+ }
+ ],
+ "count": 17,
+ "page_size": 50
+}
+```
+
+
+## `POST /api/v1/pleroma/admin/oauth_app`
+
+### Create OAuth App
+
+- Params:
+ - `name`
+ - `redirect_uris`
+ - `scopes`
+ - *optional* `website`
+ - *optional* `trusted`
+
+- Response:
+
+```json
+{
+ "id": 1,
+ "name": "App name",
+ "client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
+ "client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
+ "redirect_uri": "https://example.com/oauth-callback",
+ "website": "https://example.com",
+ "trusted": true
+}
+```
+
+- On failure:
+```json
+{
+ "redirect_uris": "can't be blank",
+ "name": "can't be blank"
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/oauth_app/:id`
+
+### Update OAuth App
+
+- Params:
+ - *optional* `name`
+ - *optional* `redirect_uris`
+ - *optional* `scopes`
+ - *optional* `website`
+ - *optional* `trusted`
+
+- Response:
+
+```json
+{
+ "id": 1,
+ "name": "App name",
+ "client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
+ "client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
+ "redirect_uri": "https://example.com/oauth-callback",
+ "website": "https://example.com",
+ "trusted": true
+}
+```
+
+## `DELETE /api/v1/pleroma/admin/oauth_app/:id`
+
+### Delete OAuth App
+
+- Params: None
+
+- Response:
+ - On success: `204`, empty response
+ - On failure:
+ - 400 Bad Request `"Invalid parameters"` when `status` is missing
+
+## `GET /api/v1/pleroma/admin/media_proxy_caches`
+
+### Get a list of all banned MediaProxy URLs in Cachex
+
+- Authentication: required
+- Params:
+- *optional* `page`: **integer** page number
+- *optional* `page_size`: **integer** number of log entries per page (default is `50`)
+- *optional* `query`: **string** search term
+
+- Response:
+
+``` json
+{
+ "page_size": integer,
+ "count": integer,
+ "urls": [
+ "http://example.com/media/a688346.jpg",
+ "http://example.com/media/fb1f4d.jpg"
+ ]
+}
+
+```
+
+## `POST /api/v1/pleroma/admin/media_proxy_caches/delete`
+
+### Remove a banned MediaProxy URL from Cachex
+
+- Authentication: required
+- Params:
+ - `urls` (array)
+
+- Response:
+
+``` json
+{ }
+
+```
+
+## `POST /api/v1/pleroma/admin/media_proxy_caches/purge`
+
+### Purge a MediaProxy URL
+
+- Authentication: required
+- Params:
+ - `urls` (array)
+ - `ban` (boolean)
+
+- Response:
+
+``` json
+{ }
+
+```
+
+## GET /api/v1/pleroma/admin/users/:nickname/chats
+
+### List a user's chats
+
+- Params: None
+
+- Response:
+
+```json
+[
+ {
+ "sender": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "receiver": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "id" : "1",
+ "unread" : 2,
+ "last_message" : {...}, // The last message in that chat
+ "updated_at": "2020-04-21T15:11:46.000Z"
+ }
+]
+```
+
+## GET /api/v1/pleroma/admin/chats/:chat_id
+
+### View a single chat
+
+- Params: None
+
+- Response:
+
+```json
+{
+ "sender": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "receiver": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "id" : "1",
+ "unread" : 2,
+ "last_message" : {...}, // The last message in that chat
+ "updated_at": "2020-04-21T15:11:46.000Z"
+}
+```
+
+## GET /api/v1/pleroma/admin/chats/:chat_id/messages
+
+### List the messages in a chat
+
+- Params: `max_id`, `min_id`
+
+- Response:
+
+```json
+[
+ {
+ "account_id": "someflakeid",
+ "chat_id": "1",
+ "content": "Check this out :firefox:",
+ "created_at": "2020-04-21T15:11:46.000Z",
+ "emojis": [
+ {
+ "shortcode": "firefox",
+ "static_url": "https://dontbulling.me/emoji/Firefox.gif",
+ "url": "https://dontbulling.me/emoji/Firefox.gif",
+ "visible_in_picker": false
+ }
+ ],
+ "id": "13",
+ "unread": true
+ },
+ {
+ "account_id": "someflakeid",
+ "chat_id": "1",
+ "content": "Whats' up?",
+ "created_at": "2020-04-21T15:06:45.000Z",
+ "emojis": [],
+ "id": "12",
+ "unread": false
+ }
+]
+```
+
+## DELETE /api/v1/pleroma/admin/chats/:chat_id/messages/:message_id
+
+### Delete a single message
+
+- Params: None
+
+- Response:
+
+```json
+{
+ "account_id": "someflakeid",
+ "chat_id": "1",
+ "content": "Check this out :firefox:",
+ "created_at": "2020-04-21T15:11:46.000Z",
+ "emojis": [
+ {
+ "shortcode": "firefox",
+ "static_url": "https://dontbulling.me/emoji/Firefox.gif",
+ "url": "https://dontbulling.me/emoji/Firefox.gif",
+ "visible_in_picker": false
+ }
+ ],
+ "id": "13",
+ "unread": false
+}
+```
+
+## `GET /api/v1/pleroma/admin/instance_document/:document_name`
+
+### Get an instance document
+
+- Authentication: required
+
+- Response:
+
+Returns the content of the document
+
+```html
+<h1>Instance panel</h1>
+```
+
+## `PATCH /api/v1/pleroma/admin/instance_document/:document_name`
+- Params:
+ - `file` (the file to be uploaded, using multipart form data.)
+
+### Update an instance document
+
+- Authentication: required
+
+- Response:
+
+``` json
+{
+ "url": "https://example.com/instance/panel.html"
+}
+```
+
+## `DELETE /api/v1/pleroma/admin/instance_document/:document_name`
+
+### Delete an instance document
+
+- Response:
+
+``` json
+{
+ "url": "https://example.com/instance/panel.html"
+}
+```
+
+## `GET /api/v1/pleroma/admin/frontends
+
+### List available frontends
+
+- Response:
+
+```json
+[
+ {
+ "build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",
+ "git": "https://git.pleroma.social/pleroma/fedi-fe",
+ "installed": true,
+ "name": "fedi-fe",
+ "ref": "master"
+ },
+ {
+ "build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
+ "git": "https://git.pleroma.social/lambadalambda/kenoma",
+ "installed": false,
+ "name": "kenoma",
+ "ref": "master"
+ }
+]
+```
+
+## `POST /api/v1/pleroma/admin/frontends/install`
+
+### Install a frontend
+
+- Params:
+ - `name`: frontend name, required
+ - `ref`: frontend ref
+ - `file`: path to a frontend zip file
+ - `build_url`: build URL
+ - `build_dir`: build directory
+
+- Response:
+
+```json
+[
+ {
+ "build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",
+ "git": "https://git.pleroma.social/pleroma/fedi-fe",
+ "installed": true,
+ "name": "fedi-fe",
+ "ref": "master"
+ },
+ {
+ "build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
+ "git": "https://git.pleroma.social/lambadalambda/kenoma",
+ "installed": false,
+ "name": "kenoma",
+ "ref": "master"
+ }
+]
+```
+
+```json
+{
+ "error": "Could not install frontend"
+}
+```
+
+## `GET /api/v1/pleroma/admin/announcements`
+
+### List announcements
+
+- Params: `offset`, `limit`
+
+- Response: JSON, list of announcements
+
+```json
+[
+ {
+ "id": "AHDp0GBdRn1EPN5HN2",
+ "content": "some content",
+ "starts_at": null,
+ "ends_at": null,
+ "all_day": false,
+ "published_at": "2022-03-09T02:13:05",
+ "reactions": [],
+ "statuses": [],
+ "tags": [],
+ "emojis": [],
+ "updated_at": "2022-03-09T02:13:05"
+ }
+]
+```
+
+Note that this differs from the Mastodon API variant: Mastodon API only returns *active* announcements, while this returns all.
+
+## `GET /api/v1/pleroma/admin/announcements/:id`
+
+### Display one announcement
+
+- Response: JSON, one announcement
+
+```json
+{
+ "id": "AHDp0GBdRn1EPN5HN2",
+ "content": "some content",
+ "starts_at": null,
+ "ends_at": null,
+ "all_day": false,
+ "published_at": "2022-03-09T02:13:05",
+ "reactions": [],
+ "statuses": [],
+ "tags": [],
+ "emojis": [],
+ "updated_at": "2022-03-09T02:13:05"
+}
+```
+
+## `POST /api/v1/pleroma/admin/announcements`
+
+### Create an announcement
+
+- Params:
+ - `content`: string, required, announcement content
+ - `starts_at`: datetime, optional, default to null, the time when the announcement will become active (displayed to users); if it is null, the announcement will be active immediately
+ - `ends_at`: datetime, optional, default to null, the time when the announcement will become inactive (no longer displayed to users); if it is null, the announcement will be active until an admin deletes it
+ - `all_day`: boolean, optional, default to false, tells the client whether to only display dates for `starts_at` and `ends_at`
+
+- Response: JSON, created announcement
+
+```json
+{
+ "id": "AHDp0GBdRn1EPN5HN2",
+ "content": "some content",
+ "starts_at": null,
+ "ends_at": null,
+ "all_day": false,
+ "published_at": "2022-03-09T02:13:05",
+ "reactions": [],
+ "statuses": [],
+ "tags": [],
+ "emojis": [],
+ "updated_at": "2022-03-09T02:13:05"
+}
+```
+
+## `PATCH /api/v1/pleroma/admin/announcements/:id`
+
+### Change an announcement
+
+- Params: same as `POST /api/v1/pleroma/admin/announcements`, except no param is required.
+
+- Updates the announcement according to params. Missing params are kept as-is.
+
+- Response: JSON, updated announcement
+
+```json
+{
+ "id": "AHDp0GBdRn1EPN5HN2",
+ "content": "some content",
+ "starts_at": null,
+ "ends_at": null,
+ "all_day": false,
+ "published_at": "2022-03-09T02:13:05",
+ "reactions": [],
+ "statuses": [],
+ "tags": [],
+ "emojis": [],
+ "updated_at": "2022-03-09T02:13:05"
+}
+```
+
+## `DELETE /api/v1/pleroma/admin/announcements/:id`
+
+### Delete an announcement
+
+- Response: JSON, empty object
+
+```json
+{}
+```
diff --git a/docs/development/API/chats.md b/docs/development/API/chats.md
new file mode 100644
index 0000000..f50144c
--- /dev/null
+++ b/docs/development/API/chats.md
@@ -0,0 +1,255 @@
+# Chats
+
+Chats are a way to represent an IM-style conversation between two actors. They are not the same as direct messages and they are not `Status`es, even though they have a lot in common.
+
+## Why Chats?
+
+There are no 'visibility levels' in ActivityPub, their definition is purely a Mastodon convention. Direct Messaging between users on the fediverse has mostly been modeled by using ActivityPub addressing following Mastodon conventions on normal `Note` objects. In this case, a 'direct message' would be a message that has no followers addressed and also does not address the special public actor, but just the recipients in the `to` field. It would still be a `Note` and is presented with other `Note`s as a `Status` in the API.
+
+This is an awkward setup for a few reasons:
+
+- As DMs generally still follow the usual `Status` conventions, it is easy to accidentally pull somebody into a DM thread by mentioning them. (e.g. "I hate @badguy so much")
+- It is possible to go from a publicly addressed `Status` to a DM reply, back to public, then to a 'followers only' reply, and so on. This can be become very confusing, as it is unclear which user can see which part of the conversation.
+- The standard `Status` format of implicit addressing also leads to rather ugly results if you try to display the messages as a chat, because all the recipients are always mentioned by name in the message.
+- As direct messages are posted with the same api call (and usually same frontend component) as public messages, accidentally making a public message private or vice versa can happen easily. Client bugs can also lead to this, accidentally making private messages public.
+
+As a measure to improve this situation, the `Conversation` concept and related Pleroma extensions were introduced. While it made it possible to work around a few of the issues, many of the problems remained and it didn't see much adoption because it was too complicated to use correctly.
+
+## Chats explained
+For this reasons, Chats are a new and different entity, both in the API as well as in ActivityPub. A quick overview:
+
+- Chats are meant to represent an instant message conversation between two actors. For now these are only 1-on-1 conversations, but the other actor can be a group in the future.
+- Chat messages have the ActivityPub type `ChatMessage`. They are not `Note`s. Servers that don't understand them will just drop them.
+- The only addressing allowed in `ChatMessage`s is one single ActivityPub actor in the `to` field.
+- There's always only one Chat between two actors. If you start chatting with someone and later start a 'new' Chat, the old Chat will be continued.
+- `ChatMessage`s are posted with a different api, making it very hard to accidentally send a message to the wrong person.
+- `ChatMessage`s don't show up in the existing timelines.
+- Chats can never go from private to public. They are always private between the two actors.
+
+## Caveats
+
+- Chats are NOT E2E encrypted (yet). Security is still the same as email.
+
+## API
+
+In general, the way to send a `ChatMessage` is to first create a `Chat`, then post a message to that `Chat`. `Group`s will later be supported by making them a sub-type of `Account`.
+
+This is the overview of using the API. The API is also documented via OpenAPI, so you can view it and play with it by pointing SwaggerUI or a similar OpenAPI tool to `https://yourinstance.tld/api/openapi`.
+
+### Creating or getting a chat.
+
+To create or get an existing Chat for a certain recipient (identified by Account ID)
+you can call:
+
+`POST /api/v1/pleroma/chats/by-account-id/:account_id`
+
+The account id is the normal FlakeId of the user
+```
+POST /api/v1/pleroma/chats/by-account-id/someflakeid
+```
+
+If you already have the id of a chat, you can also use
+
+```
+GET /api/v1/pleroma/chats/:id
+```
+
+There will only ever be ONE Chat for you and a given recipient, so this call
+will return the same Chat if you already have one with that user.
+
+Returned data:
+
+```json
+{
+ "account": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "id" : "1",
+ "unread" : 2,
+ "last_message" : {...}, // The last message in that chat
+ "updated_at": "2020-04-21T15:11:46.000Z"
+}
+```
+
+### Marking a chat as read
+
+To mark a number of messages in a chat up to a certain message as read, you can use
+
+`POST /api/v1/pleroma/chats/:id/read`
+
+
+Parameters:
+- last_read_id: Given this id, all chat messages until this one will be marked as read. Required.
+
+
+Returned data:
+
+```json
+{
+ "account": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "id" : "1",
+ "unread" : 0,
+ "updated_at": "2020-04-21T15:11:46.000Z"
+}
+```
+
+### Marking a single chat message as read
+
+To set the `unread` property of a message to `false`
+
+`POST /api/v1/pleroma/chats/:id/messages/:message_id/read`
+
+Returned data:
+
+The modified chat message
+
+### Getting a list of Chats
+
+`GET /api/v1/pleroma/chats`
+
+This will return a list of chats that you have been involved in, sorted by their
+last update (so new chats will be at the top).
+
+Parameters:
+
+- with_muted: Include chats from muted users (boolean).
+
+Returned data:
+
+```json
+[
+ {
+ "account": {
+ "id": "someflakeid",
+ "username": "somenick",
+ ...
+ },
+ "id" : "1",
+ "unread" : 2,
+ "last_message" : {...}, // The last message in that chat
+ "updated_at": "2020-04-21T15:11:46.000Z"
+ }
+]
+```
+
+The recipient of messages that are sent to this chat is given by their AP ID.
+No pagination is implemented for now.
+
+### Getting the messages for a Chat
+
+For a given Chat id, you can get the associated messages with
+
+`GET /api/v1/pleroma/chats/:id/messages`
+
+This will return all messages, sorted by most recent to least recent. The usual
+pagination options are implemented.
+
+Returned data:
+
+```json
+[
+ {
+ "account_id": "someflakeid",
+ "chat_id": "1",
+ "content": "Check this out :firefox:",
+ "created_at": "2020-04-21T15:11:46.000Z",
+ "emojis": [
+ {
+ "shortcode": "firefox",
+ "static_url": "https://dontbulling.me/emoji/Firefox.gif",
+ "url": "https://dontbulling.me/emoji/Firefox.gif",
+ "visible_in_picker": false
+ }
+ ],
+ "id": "13",
+ "unread": true
+ },
+ {
+ "account_id": "someflakeid",
+ "chat_id": "1",
+ "content": "Whats' up?",
+ "created_at": "2020-04-21T15:06:45.000Z",
+ "emojis": [],
+ "id": "12",
+ "unread": false,
+ "idempotency_key": "75442486-0874-440c-9db1-a7006c25a31f"
+ }
+]
+```
+
+- idempotency_key: The copy of the `idempotency-key` HTTP request header that can be used for optimistic message sending. Included only during the first few minutes after the message creation.
+
+### Posting a chat message
+
+Posting a chat message for given Chat id works like this:
+
+`POST /api/v1/pleroma/chats/:id/messages`
+
+Parameters:
+- content: The text content of the message. Optional if media is attached.
+- media_id: The id of an upload that will be attached to the message.
+
+Currently, no formatting beyond basic escaping and emoji is implemented.
+
+Returned data:
+
+```json
+{
+ "account_id": "someflakeid",
+ "chat_id": "1",
+ "content": "Check this out :firefox:",
+ "created_at": "2020-04-21T15:11:46.000Z",
+ "emojis": [
+ {
+ "shortcode": "firefox",
+ "static_url": "https://dontbulling.me/emoji/Firefox.gif",
+ "url": "https://dontbulling.me/emoji/Firefox.gif",
+ "visible_in_picker": false
+ }
+ ],
+ "id": "13",
+ "unread": false
+}
+```
+
+### Deleting a chat message
+
+Deleting a chat message for given Chat id works like this:
+
+`DELETE /api/v1/pleroma/chats/:chat_id/messages/:message_id`
+
+Returned data is the deleted message.
+
+### Notifications
+
+There's a new `pleroma:chat_mention` notification, which has this form. It is not given out in the notifications endpoint by default, you need to explicitly request it with `include_types[]=pleroma:chat_mention`:
+
+```json
+{
+ "id": "someid",
+ "type": "pleroma:chat_mention",
+ "account": { ... } // User account of the sender,
+ "chat_message": {
+ "chat_id": "1",
+ "id": "10",
+ "content": "Hello",
+ "account_id": "someflakeid",
+ "unread": false
+ },
+ "created_at": "somedate"
+}
+```
+
+### Streaming
+
+There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
+
+### Web Push
+
+If you want to receive push messages for this type, you'll need to add the `pleroma:chat_mention` type to your alerts in the push subscription.
diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md
new file mode 100644
index 0000000..4007c63
--- /dev/null
+++ b/docs/development/API/differences_in_mastoapi_responses.md
@@ -0,0 +1,390 @@
+# Differences in Mastodon API responses from vanilla Mastodon
+
+A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma <version>)" present in `version` field in response from `/api/v1/instance`
+
+## Flake IDs
+
+Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However, just like Mastodon's ids, they are lexically sortable strings
+
+## Timelines
+
+Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
+
+Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
+
+Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
+
+Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
+
+Home, public, hashtag & list timelines accept these parameters:
+
+- `only_media`: show only statuses with media attached
+- `local`: show only local statuses
+- `remote`: show only remote statuses
+
+## Statuses
+
+- `visibility`: has additional possible values `list` and `local` (for local-only statuses)
+
+Has these additional fields under the `pleroma` object:
+
+- `local`: true if the post was made on the local instance
+- `conversation_id`: the ID of the AP context the status is associated with (if any)
+- `direct_conversation_id`: the ID of the Mastodon direct message conversation the status is associated with (if any)
+- `in_reply_to_account_acct`: the `acct` property of User entity for replied user (if any)
+- `content`: a map consisting of alternate representations of the `content` property with the key being its mimetype. Currently, the only alternate representation supported is `text/plain`
+- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being its mimetype. Currently, the only alternate representation supported is `text/plain`
+- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
+- `thread_muted`: true if the thread the post belongs to is muted
+- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
+- `parent_visible`: If the parent of this post is visible to the user or not.
+- `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise.
+
+The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
+
+- `content_type`: The content type of the status source.
+
+## Scheduled statuses
+
+Has these additional fields in `params`:
+
+- `expires_in`: the number of seconds the posted activity should expire in.
+
+## Media Attachments
+
+Has these additional fields under the `pleroma` object:
+
+- `mime_type`: mime type of the attachment.
+
+### Attachment cap
+
+Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
+
+### Limitations
+
+Pleroma does not process remote images and therefore cannot include fields such as `meta` and `blurhash`. It does not support focal points or aspect ratios. The frontend is expected to handle it.
+
+## Accounts
+
+The `id` parameter can also be the `nickname` of the user. This only works in these endpoints, not the deeper nested ones for following etc.
+
+- `/api/v1/accounts/:id`
+- `/api/v1/accounts/:id/statuses`
+
+`/api/v1/accounts/:id/statuses` endpoint accepts these parameters:
+
+- `pinned`: include only pinned statuses
+- `tagged`: with tag
+- `only_media`: include only statuses with media attached
+- `with_muted`: include statuses/reactions from muted accounts
+- `exclude_reblogs`: exclude reblogs
+- `exclude_replies`: exclude replies
+- `exclude_visibilities`: exclude visibilities
+
+Endpoints which accept `with_relationships` parameter:
+
+- `/api/v1/accounts/:id`
+- `/api/v1/accounts/:id/followers`
+- `/api/v1/accounts/:id/following`
+- `/api/v1/mutes`
+
+Has these additional fields under the `pleroma` object:
+
+- `ap_id`: nullable URL string, ActivityPub id of the user
+- `background_image`: nullable URL string, background image of the user
+- `tags`: Lists an array of tags for the user
+- `relationship` (object): Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/
+- `is_moderator`: boolean, nullable, true if user is a moderator
+- `is_admin`: boolean, nullable, true if user is an admin
+- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
+- `hide_favorites`: boolean, true when the user has hiding favorites enabled
+- `hide_followers`: boolean, true when the user has follower hiding enabled
+- `hide_follows`: boolean, true when the user has follow hiding enabled
+- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
+- `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
+- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials`
+- `chat_token`: The token needed for Pleroma shoutbox. Only returned in `/api/v1/accounts/verify_credentials`
+- `deactivated`: boolean, true when the user is deactivated
+- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
+- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
+- `unread_notifications_count`: The count of unread notifications. Only returned to the account owner.
+- `notification_settings`: object, can be absent. See `/api/v1/pleroma/notification_settings` for the parameters/keys returned.
+- `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user
+- `favicon`: nullable URL string, Favicon image of the user's instance
+
+### Source
+
+Has these additional fields under the `pleroma` object:
+
+- `show_role`: boolean, nullable, true when the user wants his role (e.g admin, moderator) to be shown
+- `no_rich_text` - boolean, nullable, true when html tags are stripped from all statuses requested from the API
+- `discoverable`: boolean, true when the user allows external services (search bots) etc. to index / list the account (regardless of this setting, user will still appear in regular search results)
+- `actor_type`: string, the type of this account.
+
+## Conversations
+
+Has an additional field under the `pleroma` object:
+
+- `recipients`: The list of the recipients of this Conversation. These will be addressed when replying to this conversation.
+
+## GET `/api/v1/conversations`
+
+Accepts additional parameters:
+
+- `recipients`: Only return conversations with the given recipients (a list of user ids). Usage example: `GET /api/v1/conversations?recipients[]=1&recipients[]=2`
+
+## Account Search
+
+Behavior has changed:
+
+- `/api/v1/accounts/search`: Does not require authentication
+
+## Search (global)
+
+Unlisted posts are available in search results, they are considered to be public posts that shouldn't be shown in local/federated timeline.
+
+## Notifications
+
+Has these additional fields under the `pleroma` object:
+
+- `is_seen`: true if the notification was read by the user
+
+### Move Notification
+
+The `type` value is `move`. Has an additional field:
+
+- `target`: new account
+
+### EmojiReact Notification
+
+The `type` value is `pleroma:emoji_reaction`. Has these fields:
+
+- `emoji`: The used emoji
+- `account`: The account of the user who reacted
+- `status`: The status that was reacted on
+
+### ChatMention Notification (not default)
+
+This notification has to be requested explicitly.
+
+The `type` value is `pleroma:chat_mention`
+
+- `account`: The account who sent the message
+- `chat_message`: The chat message
+
+### Report Notification (not default)
+
+This notification has to be requested explicitly.
+
+The `type` value is `pleroma:report`
+
+- `account`: The account who reported
+- `report`: The report
+
+## GET `/api/v1/notifications`
+
+Accepts additional parameters:
+
+- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
+- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:chat_mention`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
+
+## DELETE `/api/v1/notifications/destroy_multiple`
+
+An endpoint to delete multiple statuses by IDs.
+
+Required parameters:
+
+- `ids`: array of activity ids
+
+Usage example: `DELETE /api/v1/notifications/destroy_multiple/?ids[]=1&ids[]=2`.
+
+Returns on success: 200 OK `{}`
+
+## POST `/api/v1/statuses`
+
+Additional parameters can be added to the JSON body/Form data:
+
+- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entity would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
+- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
+- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply.
+- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
+- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
+- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
+
+## GET `/api/v1/statuses`
+
+An endpoint to get multiple statuses by IDs.
+
+Required parameters:
+
+- `ids`: array of activity ids
+
+Usage example: `GET /api/v1/statuses/?ids[]=1&ids[]=2`.
+
+Returns: array of Status.
+
+The maximum number of statuses is limited to 100 per request.
+
+## PATCH `/api/v1/accounts/update_credentials`
+
+Additional parameters can be added to the JSON body/Form data:
+
+- `no_rich_text` - if true, html tags are stripped from all statuses requested from the API
+- `hide_followers` - if true, user's followers will be hidden
+- `hide_follows` - if true, user's follows will be hidden
+- `hide_followers_count` - if true, user's follower count will be hidden
+- `hide_follows_count` - if true, user's follow count will be hidden
+- `hide_favorites` - if true, user's favorites timeline will be hidden
+- `show_role` - if true, user's role (e.g admin, moderator) will be exposed to anyone in the API
+- `default_scope` - the scope returned under `privacy` key in Source subentity
+- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
+- `skip_thread_containment` - if true, skip filtering out broken threads
+- `allow_following_move` - if true, allows automatically follow moved following accounts
+- `also_known_as` - array of ActivityPub IDs, needed for following move
+- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
+- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
+- `actor_type` - the type of this account.
+- `accepts_chat_messages` - if false, this account will reject all chat messages.
+- `language` - user's preferred language for receiving emails (digest, confirmation, etc.)
+
+All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.
+
+### Pleroma Settings Store
+
+Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
+
+The parameter should have a form of `{frontend_name: {...}}`, with `frontend_name` identifying your type of client, e.g. `pleroma_fe`. It will overwrite everything under this property, but will not overwrite other frontend's settings.
+
+This information is returned in the `/api/v1/accounts/verify_credentials` endpoint.
+
+## Authentication
+
+*Pleroma supports refreshing tokens.*
+
+### POST `/oauth/token`
+
+You can obtain access tokens for a user in a few additional ways.
+
+#### Refreshing a token
+
+To obtain a new access token from a refresh token, pass `grant_type=refresh_token` with the following extra parameters:
+
+- `refresh_token`: The refresh token.
+
+#### Getting a token with a password
+
+To obtain a token from a user's password, pass `grant_type=password` with the following extra parameters:
+
+- `username`: Username to authenticate.
+- `password`: The user's password.
+
+#### Response body
+
+Additional fields are returned in the response:
+
+- `id`: The primary key of this token in Pleroma's database.
+- `me` (user tokens only): The ActivityPub ID of the user who owns the token.
+
+## Account Registration
+
+`POST /api/v1/accounts`
+
+Has these additional parameters (which are the same as in Pleroma-API):
+
+- `fullname`: optional
+- `bio`: optional
+- `captcha_solution`: optional, contains provider-specific captcha solution,
+- `captcha_token`: optional, contains provider-specific captcha token
+- `captcha_answer_data`: optional, contains provider-specific captcha data
+- `token`: invite token required when the registrations aren't public.
+- `language`: optional, user's preferred language for receiving emails (digest, confirmation, etc.), default to the language set in the `userLanguage` cookies or `Accept-Language` header.
+
+## Instance
+
+`GET /api/v1/instance` has additional fields
+
+- `max_toot_chars`: The maximum characters per post
+- `chat_limit`: The maximum characters per chat message
+- `description_limit`: The maximum characters per image description
+- `poll_limits`: The limits of polls
+- `upload_limit`: The maximum upload file size
+- `avatar_upload_limit`: The same for avatars
+- `background_upload_limit`: The same for backgrounds
+- `banner_upload_limit`: The same for banners
+- `background_image`: A background image that frontends can use
+- `pleroma.metadata.features`: A list of supported features
+- `pleroma.metadata.federation`: The federation restrictions of this instance
+- `pleroma.metadata.fields_limits`: A list of values detailing the length and count limitation for various instance-configurable fields.
+- `pleroma.metadata.post_formats`: A list of the allowed post format types
+- `vapid_public_key`: The public key needed for push messages
+
+## Push Subscription
+
+`POST /api/v1/push/subscription`
+`PUT /api/v1/push/subscription`
+
+Permits these additional alert types:
+
+- pleroma:chat_mention
+- pleroma:emoji_reaction
+
+## Markers
+
+Has these additional fields under the `pleroma` object:
+
+- `unread_count`: contains number unread notifications
+
+## Streaming
+
+### Chats
+
+There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
+
+### Remote timelines
+
+For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
+
+### Follow relationships updates
+
+Pleroma streams follow relationships updates as `pleroma:follow_relationships_update` events to the `user` stream.
+
+The message payload consist of:
+
+- `state`: a relationship state, one of `follow_pending`, `follow_accept` or `follow_reject`.
+
+- `follower` and `following` maps with following fields:
+ - `id`: user ID
+ - `follower_count`: follower count
+ - `following_count`: following count
+
+## User muting and thread muting
+
+Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
+
+## Not implemented
+
+Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
+
+### Suggestions
+
+*Added in Mastodon 2.4.3*
+
+- `GET /api/v1/suggestions`: Returns an empty array, `[]`
+
+### Trends
+
+*Added in Mastodon 3.0.0*
+
+- `GET /api/v1/trends`: Returns an empty array, `[]`
+
+### Identity proofs
+
+*Added in Mastodon 2.8.0*
+
+- `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
+
+### Featured tags
+
+*Added in Mastodon 3.0.0*
+
+- `GET /api/v1/featured_tags`: Returns HTTP 404
diff --git a/docs/development/API/nodeinfo.md b/docs/development/API/nodeinfo.md
new file mode 100644
index 0000000..0f998a1
--- /dev/null
+++ b/docs/development/API/nodeinfo.md
@@ -0,0 +1,347 @@
+# Nodeinfo
+
+See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
+
+## `/.well-known/nodeinfo`
+### The well-known path
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+ "links":[
+ {
+ "href":"https://example.com/nodeinfo/2.0.json",
+ "rel":"http://nodeinfo.diaspora.software/ns/schema/2.0"
+ },
+ {
+ "href":"https://example.com/nodeinfo/2.1.json",
+ "rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"
+ }
+ ]
+}
+```
+
+## `/nodeinfo/2.0.json`
+### Nodeinfo 2.0
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+ "metadata":{
+ "accountActivationRequired":false,
+ "features":[
+ "pleroma_api",
+ "mastodon_api",
+ "mastodon_api_streaming",
+ "polls",
+ "pleroma_explicit_addressing",
+ "shareable_emoji_packs",
+ "multifetch",
+ "pleroma:api/v1/notifications:include_types_filter",
+ "chat",
+ "shout",
+ "relay",
+ "pleroma_emoji_reactions",
+ "pleroma_chat_messages"
+ ],
+ "federation":{
+ "enabled":true,
+ "exclusions":false,
+ "mrf_hashtag":{
+ "federated_timeline_removal":[
+
+ ],
+ "reject":[
+
+ ],
+ "sensitive":[
+ "nsfw"
+ ]
+ },
+ "mrf_object_age":{
+ "actions":[
+ "delist",
+ "strip_followers"
+ ],
+ "threshold":604800
+ },
+ "mrf_policies":[
+ "ObjectAgePolicy",
+ "TagPolicy",
+ "HashtagPolicy"
+ ],
+ "quarantined_instances":[
+
+ ]
+ },
+ "fieldsLimits":{
+ "maxFields":10,
+ "maxRemoteFields":20,
+ "nameLength":512,
+ "valueLength":2048
+ },
+ "invitesEnabled":false,
+ "mailerEnabled":false,
+ "nodeDescription":"Pleroma: An efficient and flexible fediverse server",
+ "nodeName":"Example",
+ "pollLimits":{
+ "max_expiration":31536000,
+ "max_option_chars":200,
+ "max_options":20,
+ "min_expiration":0
+ },
+ "postFormats":[
+ "text/plain",
+ "text/html",
+ "text/markdown",
+ "text/bbcode"
+ ],
+ "private":false,
+ "restrictedNicknames":[
+ ".well-known",
+ "~",
+ "about",
+ "activities",
+ "api",
+ "auth",
+ "check_password",
+ "dev",
+ "friend-requests",
+ "inbox",
+ "internal",
+ "main",
+ "media",
+ "nodeinfo",
+ "notice",
+ "oauth",
+ "objects",
+ "ostatus_subscribe",
+ "pleroma",
+ "proxy",
+ "push",
+ "registration",
+ "relay",
+ "settings",
+ "status",
+ "tag",
+ "user-search",
+ "user_exists",
+ "users",
+ "web",
+ "verify_credentials",
+ "update_credentials",
+ "relationships",
+ "search",
+ "confirmation_resend",
+ "mfa"
+ ],
+ "skipThreadContainment":true,
+ "staffAccounts":[
+ "https://example.com/users/admin",
+ "https://example.com/users/staff"
+ ],
+ "suggestions":{
+ "enabled":false
+ },
+ "uploadLimits":{
+ "avatar":2000000,
+ "background":4000000,
+ "banner":4000000,
+ "general":16000000
+ }
+ },
+ "openRegistrations":true,
+ "protocols":[
+ "activitypub"
+ ],
+ "services":{
+ "inbound":[
+
+ ],
+ "outbound":[
+
+ ]
+ },
+ "software":{
+ "name":"pleroma",
+ "version":"2.4.1"
+ },
+ "usage":{
+ "localPosts":27,
+ "users":{
+ "activeHalfyear":129,
+ "activeMonth":70,
+ "total":235
+ }
+ },
+ "version":"2.0"
+}
+```
+
+## `/nodeinfo/2.1.json`
+### Nodeinfo 2.1
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+ "metadata":{
+ "accountActivationRequired":false,
+ "features":[
+ "pleroma_api",
+ "mastodon_api",
+ "mastodon_api_streaming",
+ "polls",
+ "pleroma_explicit_addressing",
+ "shareable_emoji_packs",
+ "multifetch",
+ "pleroma:api/v1/notifications:include_types_filter",
+ "chat",
+ "shout",
+ "relay",
+ "pleroma_emoji_reactions",
+ "pleroma_chat_messages"
+ ],
+ "federation":{
+ "enabled":true,
+ "exclusions":false,
+ "mrf_hashtag":{
+ "federated_timeline_removal":[
+
+ ],
+ "reject":[
+
+ ],
+ "sensitive":[
+ "nsfw"
+ ]
+ },
+ "mrf_object_age":{
+ "actions":[
+ "delist",
+ "strip_followers"
+ ],
+ "threshold":604800
+ },
+ "mrf_policies":[
+ "ObjectAgePolicy",
+ "TagPolicy",
+ "HashtagPolicy"
+ ],
+ "quarantined_instances":[
+
+ ]
+ },
+ "fieldsLimits":{
+ "maxFields":10,
+ "maxRemoteFields":20,
+ "nameLength":512,
+ "valueLength":2048
+ },
+ "invitesEnabled":false,
+ "mailerEnabled":false,
+ "nodeDescription":"Pleroma: An efficient and flexible fediverse server",
+ "nodeName":"Example",
+ "pollLimits":{
+ "max_expiration":31536000,
+ "max_option_chars":200,
+ "max_options":20,
+ "min_expiration":0
+ },
+ "postFormats":[
+ "text/plain",
+ "text/html",
+ "text/markdown",
+ "text/bbcode"
+ ],
+ "private":false,
+ "restrictedNicknames":[
+ ".well-known",
+ "~",
+ "about",
+ "activities",
+ "api",
+ "auth",
+ "check_password",
+ "dev",
+ "friend-requests",
+ "inbox",
+ "internal",
+ "main",
+ "media",
+ "nodeinfo",
+ "notice",
+ "oauth",
+ "objects",
+ "ostatus_subscribe",
+ "pleroma",
+ "proxy",
+ "push",
+ "registration",
+ "relay",
+ "settings",
+ "status",
+ "tag",
+ "user-search",
+ "user_exists",
+ "users",
+ "web",
+ "verify_credentials",
+ "update_credentials",
+ "relationships",
+ "search",
+ "confirmation_resend",
+ "mfa"
+ ],
+ "skipThreadContainment":true,
+ "staffAccounts":[
+ "https://example.com/users/admin",
+ "https://example.com/users/staff"
+ ],
+ "suggestions":{
+ "enabled":false
+ },
+ "uploadLimits":{
+ "avatar":2000000,
+ "background":4000000,
+ "banner":4000000,
+ "general":16000000
+ }
+ },
+ "openRegistrations":true,
+ "protocols":[
+ "activitypub"
+ ],
+ "services":{
+ "inbound":[
+
+ ],
+ "outbound":[
+
+ ]
+ },
+ "software":{
+ "name":"pleroma",
+ "repository":"https://git.pleroma.social/pleroma/pleroma",
+ "version":"2.4.1"
+ },
+ "usage":{
+ "localPosts":27,
+ "users":{
+ "activeHalfyear":129,
+ "activeMonth":70,
+ "total":235
+ }
+ },
+ "version":"2.1"
+}
+```
+
diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md
new file mode 100644
index 0000000..47fcb74
--- /dev/null
+++ b/docs/development/API/pleroma_api.md
@@ -0,0 +1,766 @@
+# Pleroma API
+
+Requests that require it can be authenticated with [an OAuth token](https://tools.ietf.org/html/rfc6749), the `_pleroma_key` cookie, or [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization).
+
+Request parameters can be passed via [query strings](https://en.wikipedia.org/wiki/Query_string) or as [form data](https://www.w3.org/TR/html401/interact/forms.html). Files must be uploaded as `multipart/form-data`.
+
+The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/api/pleroma/*` will be deprecated in the future).
+
+## `/api/v1/pleroma/emoji`
+### Lists the custom emoji on that server.
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+```json
+{
+ "girlpower": {
+ "tags": [
+ "Finmoji"
+ ],
+ "image_url": "/finmoji/128px/girlpower-128.png"
+ },
+ "education": {
+ "tags": [
+ "Finmoji"
+ ],
+ "image_url": "/finmoji/128px/education-128.png"
+ },
+ "finnishlove": {
+ "tags": [
+ "Finmoji"
+ ],
+ "image_url": "/finmoji/128px/finnishlove-128.png"
+ }
+}
+```
+* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
+
+## `/api/pleroma/follow_import`
+### Imports your follows, for example from a Mastodon CSV file.
+* Method: `POST`
+* Authentication: required
+* Params:
+ * `list`: STRING or FILE containing a whitespace-separated list of accounts to follow
+* Response: HTTP 200 on success, 500 on error
+* Note: Users that can't be followed are silently skipped.
+
+## `/api/pleroma/blocks_import`
+### Imports your blocks.
+* Method: `POST`
+* Authentication: required
+* Params:
+ * `list`: STRING or FILE containing a whitespace-separated list of accounts to block
+* Response: HTTP 200 on success, 500 on error
+
+## `/api/pleroma/mutes_import`
+### Imports your mutes.
+* Method: `POST`
+* Authentication: required
+* Params:
+ * `list`: STRING or FILE containing a whitespace-separated list of accounts to mute
+* Response: HTTP 200 on success, 500 on error
+
+## `/api/v1/pleroma/captcha`
+### Get a new captcha
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: Provider specific JSON, the only guaranteed parameter is `type`
+* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}`
+
+## `/api/pleroma/delete_account`
+### Delete an account
+* Method `POST`
+* Authentication: required
+* Params:
+ * `password`: user's password
+* Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
+* Example response: `{"error": "Invalid password."}`
+
+## `/api/pleroma/disable_account`
+### Disable an account
+* Method `POST`
+* Authentication: required
+* Params:
+ * `password`: user's password
+* Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise
+* Example response: `{"error": "Invalid password."}`
+
+## `/api/pleroma/accounts/mfa`
+#### Gets current MFA settings
+* method: `GET`
+* Authentication: required
+* OAuth scope: `read:security`
+* Response: JSON. Returns `{"settings": {"enabled": "false", "totp": false }}`
+* Note: `enabled` is whether multi-factor auth is enabled for the user in general, while `totp` is one type of MFA.
+
+## `/api/pleroma/accounts/mfa/setup/totp`
+#### Pre-setup the MFA/TOTP method
+* method: `GET`
+* Authentication: required
+* OAuth scope: `write:security`
+* Response: JSON. Returns `{"key": [secret_key], "provisioning_uri": "[qr code uri]" }` when successful, otherwise returns HTTP 422 `{"error": "error_msg"}`
+
+## `/api/pleroma/accounts/mfa/confirm/totp`
+#### Confirms & enables MFA/TOTP support for user account.
+* method: `POST`
+* Authentication: required
+* OAuth scope: `write:security`
+* Params:
+ * `password`: user's password
+ * `code`: token from TOTP App
+* Response: JSON. Returns `{}` if the enable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
+
+
+## `/api/pleroma/accounts/mfa/totp`
+#### Disables MFA/TOTP method for user account.
+* method: `DELETE`
+* Authentication: required
+* OAuth scope: `write:security`
+* Params:
+ * `password`: user's password
+* Response: JSON. Returns `{}` if the disable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
+* Example response: `{"error": "Invalid password."}`
+
+## `/api/pleroma/accounts/mfa/backup_codes`
+#### Generstes backup codes MFA for user account.
+* method: `GET`
+* Authentication: required
+* OAuth scope: `write:security`
+* Response: JSON. Returns `{"codes": codes}`when successful, otherwise HTTP 422 `{"error": "[error message]"}`
+
+## `/api/v1/pleroma/admin/`
+See [Admin-API](admin_api.md)
+
+## `/api/v1/pleroma/notifications/read`
+### Mark notifications as read
+* Method `POST`
+* Authentication: required
+* Params (mutually exclusive):
+ * `id`: a single notification id to read
+ * `max_id`: read all notifications up to this id
+* Response: Notification entity/Array of Notification entities that were read. In case of `max_id`, only the first 80 read notifications will be returned.
+
+## `/api/v1/pleroma/accounts/:id/subscribe`
+### Subscribe to receive notifications for all statuses posted by a user
+* Method `POST`
+* Authentication: required
+* Params:
+ * `id`: account id to subscribe to
+* Response: JSON, returns a mastodon relationship object on success, otherwise returns `{"error": "error_msg"}`
+* Example response:
+```json
+{
+ "id": "abcdefg",
+ "following": true,
+ "followed_by": false,
+ "blocking": false,
+ "muting": false,
+ "muting_notifications": false,
+ "subscribing": true,
+ "notifying": true,
+ "requested": false,
+ "domain_blocking": false,
+ "showing_reblogs": true,
+ "endorsed": false,
+ "note": ""
+}
+```
+
+## `/api/v1/pleroma/accounts/:id/unsubscribe`
+### Unsubscribe to stop receiving notifications from user statuses
+* Method `POST`
+* Authentication: required
+* Params:
+ * `id`: account id to unsubscribe from
+* Response: JSON, returns a mastodon relationship object on success, otherwise returns `{"error": "error_msg"}`
+* Example response:
+```json
+{
+ "id": "abcdefg",
+ "following": true,
+ "followed_by": false,
+ "blocking": false,
+ "muting": false,
+ "muting_notifications": false,
+ "subscribing": false,
+ "notifying": false,
+ "requested": false,
+ "domain_blocking": false,
+ "showing_reblogs": true,
+ "endorsed": false,
+ "note": ""
+}
+```
+
+## `/api/v1/pleroma/accounts/:id/favourites`
+### Returns favorites timeline of any user
+* Method `GET`
+* Authentication: not required
+* Params:
+ * `id`: the id of the account for whom to return results
+ * `limit`: optional, the number of records to retrieve
+ * `since_id`: optional, returns results that are more recent than the specified id
+ * `max_id`: optional, returns results that are older than the specified id
+* Response: JSON, returns a list of Mastodon Status entities on success, otherwise returns `{"error": "error_msg"}`
+* Example response:
+```json
+[
+ {
+ "account": {
+ "id": "9hptFmUF3ztxYh3Svg",
+ "url": "https://pleroma.example.org/users/nick2",
+ "username": "nick2",
+ ...
+ },
+ "application": {"name": "Web", "website": null},
+ "bookmarked": false,
+ "card": null,
+ "content": "This is :moominmamma: note 0",
+ "created_at": "2019-04-15T15:42:15.000Z",
+ "emojis": [],
+ "favourited": false,
+ "favourites_count": 1,
+ "id": "9hptFmVJ02khbzYJaS",
+ "in_reply_to_account_id": null,
+ "in_reply_to_id": null,
+ "language": null,
+ "media_attachments": [],
+ "mentions": [],
+ "muted": false,
+ "pinned": false,
+ "pleroma": {
+ "content": {"text/plain": "This is :moominmamma: note 0"},
+ "conversation_id": 13679,
+ "local": true,
+ "spoiler_text": {"text/plain": "2hu"}
+ },
+ "reblog": null,
+ "reblogged": false,
+ "reblogs_count": 0,
+ "replies_count": 0,
+ "sensitive": false,
+ "spoiler_text": "2hu",
+ "tags": [{"name": "2hu", "url": "/tag/2hu"}],
+ "uri": "https://pleroma.example.org/objects/198ed2a1-7912-4482-b559-244a0369e984",
+ "url": "https://pleroma.example.org/notice/9hptFmVJ02khbzYJaS",
+ "visibility": "public"
+ }
+]
+```
+
+## `/api/v1/pleroma/accounts/update_*`
+### Set and clear account avatar, banner, and background
+
+- PATCH `/api/v1/pleroma/accounts/update_avatar`: Set/clear user avatar image
+- PATCH `/api/v1/pleroma/accounts/update_banner`: Set/clear user banner image
+- PATCH `/api/v1/pleroma/accounts/update_background`: Set/clear user background image
+
+## `/api/v1/pleroma/accounts/confirmation_resend`
+### Resend confirmation email
+* Method `POST`
+* Params:
+ * `email`: email of that needs to be verified
+* Authentication: not required
+* Response: 204 No Content
+
+## `/api/v1/pleroma/mascot`
+### Gets user mascot image
+* Method `GET`
+* Authentication: required
+
+* Response: JSON. Returns a mastodon media attachment entity.
+* Example response:
+```json
+{
+ "id": "abcdefg",
+ "url": "https://pleroma.example.org/media/abcdefg.png",
+ "type": "image",
+ "pleroma": {
+ "mime_type": "image/png"
+ }
+}
+```
+
+### Updates user mascot image
+* Method `PUT`
+* Authentication: required
+* Params:
+ * `file`: Multipart image
+* Response: JSON. Returns a mastodon media attachment entity
+ when successful, otherwise returns HTTP 415 `{"error": "error_msg"}`
+* Example response:
+```json
+{
+ "id": "abcdefg",
+ "url": "https://pleroma.example.org/media/abcdefg.png",
+ "type": "image",
+ "pleroma": {
+ "mime_type": "image/png"
+ }
+}
+```
+* Note: Behaves exactly the same as `POST /api/v1/upload`.
+ Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.
+
+## `/api/pleroma/notification_settings`
+### Updates user notification settings
+* Method `PUT`
+* Authentication: required
+* Params:
+ * `block_from_strangers`: BOOLEAN field, blocks notifications from accounts you do not follow
+ * `hide_notification_contents`: BOOLEAN field. When set to true, it removes the contents of a message from the push notification.
+* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
+
+## `/api/v1/pleroma/healthcheck`
+### Healthcheck endpoint with additional system data.
+* Method `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON, statuses (200 - healthy, 503 unhealthy).
+* Example response:
+```json
+{
+ "pool_size": 0, # database connection pool
+ "active": 0, # active processes
+ "idle": 0, # idle processes
+ "memory_used": 0.00, # Memory used
+ "healthy": true, # Instance state
+ "job_queue_stats": {} # Job queue stats
+}
+```
+
+## `/api/pleroma/change_email`
+### Change account email
+* Method `POST`
+* Authentication: required
+* Params:
+ * `password`: user's password
+ * `email`: new email
+* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise
+* Note: Currently, Mastodon has no API for changing email. If they add it in future it might be incompatible with Pleroma.
+
+## `/api/pleroma/move_account`
+### Move account
+* Method `POST`
+* Authentication: required
+* Params:
+ * `password`: user's password
+ * `target_account`: the nickname of the target account (e.g. `foo@example.org`)
+* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise
+* Note: This endpoint emits a `Move` activity to all followers of the current account. Some remote servers will automatically unfollow the current account and follow the target account upon seeing this, but this depends on the remote server implementation and cannot be guaranteed. For local followers , they will automatically unfollow and follow if and only if they have set the `allow_following_move` preference ("Allow auto-follow when following account moves").
+
+## `/api/pleroma/aliases`
+### Get aliases of the current account
+* Method `GET`
+* Authentication: required
+* Response: JSON. Returns `{"aliases": [alias, ...]}`, where `alias` is the nickname of an alias, e.g. `foo@example.org`.
+
+### Add alias to the current account
+* Method `PUT`
+* Authentication: required
+* Params:
+ * `alias`: the nickname of the alias to add, e.g. `foo@example.org`.
+* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise
+
+### Delete alias from the current account
+* Method `DELETE`
+* Authentication: required
+* Params:
+ * `alias`: the nickname of the alias to delete, e.g. `foo@example.org`.
+* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise
+
+# Pleroma Conversations
+
+Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints:
+
+1. Pleroma Conversations never add or remove recipients, unless explicitly changed by the user.
+2. Pleroma Conversations statuses can be requested by Conversation id.
+3. Pleroma Conversations can be replied to.
+
+Conversations have the additional field `recipients` under the `pleroma` key. This holds a list of all the accounts that will receive a message in this conversation.
+
+The status posting endpoint takes an additional parameter, `in_reply_to_conversation_id`, which, when set, will set the visiblity to direct and address only the people who are the recipients of that Conversation.
+
+⚠ Conversation IDs can be found in direct messages with the `pleroma.direct_conversation_id` key, do not confuse it with `pleroma.conversation_id`.
+
+## `GET /api/v1/pleroma/conversations/:id/statuses`
+### Timeline for a given conversation
+* Method `GET`
+* Authentication: required
+* Params: Like other timelines
+* Response: JSON, statuses (200 - healthy, 503 unhealthy).
+
+## `GET /api/v1/pleroma/conversations/:id`
+### The conversation with the given ID.
+* Method `GET`
+* Authentication: required
+* Params: None
+* Response: JSON, statuses (200 - healthy, 503 unhealthy).
+
+## `PATCH /api/v1/pleroma/conversations/:id`
+### Update a conversation. Used to change the set of recipients.
+* Method `PATCH`
+* Authentication: required
+* Params:
+ * `recipients`: A list of ids of users that should receive posts to this conversation. This will replace the current list of recipients, so submit the full list. The owner of owner of the conversation will always be part of the set of recipients, though.
+* Response: JSON, statuses (200 - healthy, 503 unhealthy)
+
+## `POST /api/v1/pleroma/conversations/read`
+### Marks all user's conversations as read.
+* Method `POST`
+* Authentication: required
+* Params: None
+* Response: JSON, returns a list of Mastodon Conversation entities that were marked as read (200 - healthy, 503 unhealthy).
+
+## `GET /api/v1/pleroma/emoji/pack?name=:name`
+
+### Get pack.json for the pack
+
+* Method `GET`
+* Authentication: not required
+* Params:
+ * `page`: page number for files (default 1)
+ * `page_size`: page size for files (default 30)
+* Response: JSON, pack json with `files`, `files_count` and `pack` keys with 200 status or 404 if the pack does not exist.
+
+```json
+{
+ "files": {...},
+ "files_count": 0, // emoji count in pack
+ "pack": {...}
+}
+```
+
+## `POST /api/v1/pleroma/emoji/pack?name=:name`
+
+### Creates an empty pack
+
+* Method `POST`
+* Authentication: required (admin)
+* Params:
+ * `name`: pack name
+* Response: JSON, "ok" and 200 status or 409 if the pack with that name already exists
+
+## `PATCH /api/v1/pleroma/emoji/pack?name=:name`
+
+### Updates (replaces) pack metadata
+
+* Method `PATCH`
+* Authentication: required (admin)
+* Params:
+ * `name`: pack name
+ * `metadata`: metadata to replace the old one
+ * `license`: Pack license
+ * `homepage`: Pack home page url
+ * `description`: Pack description
+ * `fallback-src`: Fallback url to download pack from
+ * `fallback-src-sha256`: SHA256 encoded for fallback pack archive
+ * `share-files`: is pack allowed for sharing (boolean)
+* Response: JSON, updated "metadata" section of the pack and 200 status or 400 if there was a
+ problem with the new metadata (the error is specified in the "error" part of the response JSON)
+
+## `DELETE /api/v1/pleroma/emoji/pack?name=:name`
+
+### Delete a custom emoji pack
+
+* Method `DELETE`
+* Authentication: required (admin)
+* Params:
+ * `name`: pack name
+* Response: JSON, "ok" and 200 status or 500 if there was an error deleting the pack
+
+## `GET /api/v1/pleroma/emoji/packs/import`
+
+### Imports packs from filesystem
+
+* Method `GET`
+* Authentication: required (admin)
+* Params: None
+* Response: JSON, returns a list of imported packs.
+
+## `GET /api/v1/pleroma/emoji/packs/remote`
+
+### Make request to another instance for packs list
+
+* Method `GET`
+* Authentication: required (admin)
+* Params:
+ * `url`: url of the instance to get packs from
+ * `page`: page number for packs (default 1)
+ * `page_size`: page size for packs (default 50)
+* Response: JSON with the pack list, hashmap with pack name and pack contents
+
+## `POST /api/v1/pleroma/emoji/packs/download`
+
+### Download pack from another instance
+
+* Method `POST`
+* Authentication: required (admin)
+* Params:
+ * `url`: url of the instance to download from
+ * `name`: pack to download from that instance
+ * `as`: (*optional*) name how to save pack
+* Response: JSON, "ok" with 200 status if the pack was downloaded, or 500 if there were
+ errors downloading the pack
+
+## `POST /api/v1/pleroma/emoji/packs/files?name=:name`
+
+### Add new file to the pack
+
+* Method `POST`
+* Authentication: required (admin)
+* Params:
+ * `name`: pack name
+ * `file`: file needs to be uploaded with the multipart request or link to remote file.
+ * `shortcode`: (*optional*) shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename.
+ * `filename`: (*optional*) new emoji file name. If not specified will be taken from original filename.
+* Response: JSON, list of files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
+
+## `PATCH /api/v1/pleroma/emoji/packs/files?name=:name`
+
+### Update emoji file from pack
+
+* Method `PATCH`
+* Authentication: required (admin)
+* Params:
+ * `name`: pack name
+ * `shortcode`: emoji file shortcode
+ * `new_shortcode`: new emoji file shortcode
+ * `new_filename`: new filename for emoji file
+ * `force`: (*optional*) with true value to overwrite existing emoji with new shortcode
+* Response: JSON, list with updated files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
+
+## `DELETE /api/v1/pleroma/emoji/packs/files?name=:name`
+
+### Delete emoji file from pack
+
+* Method `DELETE`
+* Authentication: required (admin)
+* Params:
+ * `name`: pack name
+ * `shortcode`: emoji file shortcode
+* Response: JSON, list with updated files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
+
+## `GET /api/v1/pleroma/emoji/packs`
+
+### Lists local custom emoji packs
+
+* Method `GET`
+* Authentication: not required
+* Params:
+ * `page`: page number for packs (default 1)
+ * `page_size`: page size for packs (default 50)
+* Response: `packs` key with JSON hashmap of pack name to pack contents and `count` key for count of packs.
+
+```json
+{
+ "packs": {
+ "pack_name": {...}, // pack contents
+ ...
+ },
+ "count": 0 // packs count
+}
+```
+
+## `GET /api/v1/pleroma/emoji/packs/archive?name=:name`
+
+### Requests a local pack archive from the instance
+
+* Method `GET`
+* Authentication: not required
+* Params:
+ * `name`: pack name
+* Response: the archive of the pack with a 200 status code, 403 if the pack is not set as shared,
+ 404 if the pack does not exist
+
+## `GET /api/v1/pleroma/accounts/:id/scrobbles`
+### Requests a list of current and recent Listen activities for an account
+* Method `GET`
+* Authentication: not required
+* Params: None
+* Response: An array of media metadata entities.
+* Example response:
+```json
+[
+ {
+ "account": {...},
+ "id": "1234",
+ "title": "Some Title",
+ "artist": "Some Artist",
+ "album": "Some Album",
+ "length": 180000,
+ "created_at": "2019-09-28T12:40:45.000Z"
+ }
+]
+```
+
+## `POST /api/v1/pleroma/scrobble`
+### Creates a new Listen activity for an account
+* Method `POST`
+* Authentication: required
+* Params:
+ * `title`: the title of the media playing
+ * `album`: the album of the media playing [optional]
+ * `artist`: the artist of the media playing [optional]
+ * `length`: the length of the media playing [optional]
+* Response: the newly created media metadata entity representing the Listen activity
+
+# Emoji Reactions
+
+Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character. To detect the presence of this feature, you can check `pleroma_emoji_reactions` entry in the features list of nodeinfo.
+
+## `PUT /api/v1/pleroma/statuses/:id/reactions/:emoji`
+### React to a post with a unicode emoji
+* Method: `PUT`
+* Authentication: required
+* Params: `emoji`: A unicode RGI emoji or a regional indicator
+* Response: JSON, the status.
+
+## `DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji`
+### Remove a reaction to a post with a unicode emoji
+* Method: `DELETE`
+* Authentication: required
+* Params: `emoji`: A unicode RGI emoji or a regional indicator
+* Response: JSON, the status.
+
+## `GET /api/v1/pleroma/statuses/:id/reactions`
+### Get an object of emoji to account mappings with accounts that reacted to the post
+* Method: `GET`
+* Authentication: optional
+* Params: None
+* Response: JSON, a list of emoji/account list tuples, sorted by emoji insertion date, in ascending order, e.g, the first emoji in the list is the oldest.
+* Example Response:
+```json
+[
+ {"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]},
+ {"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
+]
+```
+
+## `GET /api/v1/pleroma/statuses/:id/reactions/:emoji`
+### Get an object of emoji to account mappings with accounts that reacted to the post for a specific emoji
+* Method: `GET`
+* Authentication: optional
+* Params: None
+* Response: JSON, a list of emoji/account list tuples
+* Example Response:
+```json
+[
+ {"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
+]
+```
+
+## `POST /api/v1/pleroma/backups`
+### Create a user backup archive
+
+* Method: `POST`
+* Authentication: required
+* Params: none
+* Response: JSON
+* Example response:
+
+```json
+[{
+ "content_type": "application/zip",
+ "file_size": 0,
+ "inserted_at": "2020-09-10T16:18:03.000Z",
+ "processed": false,
+ "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
+}]
+```
+
+## `GET /api/v1/pleroma/backups`
+### Lists user backups
+
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+
+```json
+[{
+ "content_type": "application/zip",
+ "file_size": 55457,
+ "inserted_at": "2020-09-10T16:18:03.000Z",
+ "processed": true,
+ "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
+}]
+```
+
+## `GET /api/oauth_tokens`
+### Retrieve a list of active sessions for the user
+* Method: `GET`
+* Authentication: required
+* Params: none
+* Response: JSON
+* Example response:
+
+```json
+[
+ {
+ "app_name": "Pleroma FE",
+ "id": 9275,
+ "valid_until": "2121-11-24T15:51:08.234234"
+ },
+ {
+ "app_name": "Patron",
+ "id": 8805,
+ "valid_until": "2121-10-26T18:09:59.857150"
+ },
+ {
+ "app_name": "Soapbox FE",
+ "id": 9727,
+ "valid_until": "2121-12-25T16:52:39.692877"
+ }
+]
+```
+
+## `DELETE /api/oauth_tokens/:id`
+### Revoke a user session by its ID
+* Method: `DELETE`
+* Authentication: required
+* Params: none
+* Response: HTTP 200 on success, 500 on error
+
+## `/api/v1/pleroma/settings/:app`
+### Gets settings for some application
+* Method `GET`
+* Authentication: `read:accounts`
+
+* Response: JSON. The settings for that application, or empty object if there is none.
+* Example response:
+```json
+{
+ "some key": "some value"
+}
+```
+
+### Updates settings for some application
+* Method `PATCH`
+* Authentication: `write:accounts`
+* Request body: JSON object. The object will be merged recursively with old settings. If some field is set to null, it is removed.
+* Example request:
+```json
+{
+ "some key": "some value",
+ "key to remove": null,
+ "nested field": {
+ "some key": "some value",
+ "key to remove": null
+ }
+}
+```
+* Response: JSON. Updated (merged) settings for that application.
+* Example response:
+```json
+{
+ "some key": "some value",
+ "nested field": {
+ "some key": "some value",
+ }
+}
+```
diff --git a/docs/development/API/prometheus.md b/docs/development/API/prometheus.md
new file mode 100644
index 0000000..a5158d9
--- /dev/null
+++ b/docs/development/API/prometheus.md
@@ -0,0 +1,44 @@
+# Prometheus Metrics
+
+Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
+
+Config example:
+
+```
+config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
+ enabled: true,
+ auth: {:basic, "myusername", "mypassword"},
+ ip_whitelist: ["127.0.0.1"],
+ path: "/api/pleroma/app_metrics",
+ format: :text
+```
+
+* `enabled` (Pleroma extension) enables the endpoint
+* `ip_whitelist` (Pleroma extension) could be used to restrict access only to specified IPs
+* `auth` sets the authentication (`false` for no auth; configurable to HTTP Basic Auth, see [prometheus-plugs](https://github.com/deadtrickster/prometheus-plugs#exporting) documentation)
+* `format` sets the output format (`:text` or `:protobuf`)
+* `path` sets the path to app metrics page
+
+
+## `/api/pleroma/app_metrics`
+
+### Exports Prometheus application metrics
+
+* Method: `GET`
+* Authentication: not required by default (see configuration options above)
+* Params: none
+* Response: text
+
+## Grafana
+
+### Config example
+
+The following is a config example to use with [Grafana](https://grafana.com)
+
+```
+ - job_name: 'beam'
+ metrics_path: /api/pleroma/app_metrics
+ scheme: https
+ static_configs:
+ - targets: ['pleroma.soykaf.com']
+```
diff --git a/docs/development/ap_extensions.md b/docs/development/ap_extensions.md
new file mode 100644
index 0000000..3d1caeb
--- /dev/null
+++ b/docs/development/ap_extensions.md
@@ -0,0 +1,65 @@
+# AP Extensions
+## Actor endpoints
+
+The following endpoints are additionally present into our actors.
+
+- `oauthRegistrationEndpoint` (`http://litepub.social/ns#oauthRegistrationEndpoint`)
+- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`)
+
+### oauthRegistrationEndpoint
+
+Points to MastodonAPI `/api/v1/apps` for now.
+
+See <https://docs.joinmastodon.org/methods/apps/>
+
+### uploadMedia
+
+Inspired by <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>, it is part of the ActivityStreams namespace because it used to be part of the ActivityPub specification and got removed from it.
+
+Content-Type: multipart/form-data
+
+Parameters:
+- (required) `file`: The file being uploaded
+- (optionnal) `description`: A plain-text description of the media, for accessibility purposes.
+
+Response: HTTP 201 Created with the object into the body, no `Location` header provided as it doesn't have an `id`
+
+The object given in the reponse should then be inserted into an Object's `attachment` field.
+
+## ChatMessages
+
+`ChatMessage`s are the messages sent in 1-on-1 chats. They are similar to
+`Note`s, but the addresing is done by having a single AP actor in the `to`
+field. Addressing multiple actors is not allowed. These messages are always
+private, there is no public version of them. They are created with a `Create`
+activity.
+
+They are part of the `litepub` namespace as `http://litepub.social/ns#ChatMessage`.
+
+Example:
+
+```json
+{
+ "actor": "http://2hu.gensokyo/users/raymoo",
+ "id": "http://2hu.gensokyo/objects/1",
+ "object": {
+ "attributedTo": "http://2hu.gensokyo/users/raymoo",
+ "content": "You expected a cute girl? Too bad.",
+ "id": "http://2hu.gensokyo/objects/2",
+ "published": "2020-02-12T14:08:20Z",
+ "to": [
+ "http://2hu.gensokyo/users/marisa"
+ ],
+ "type": "ChatMessage"
+ },
+ "published": "2018-02-12T14:08:20Z",
+ "to": [
+ "http://2hu.gensokyo/users/marisa"
+ ],
+ "type": "Create"
+}
+```
+
+This setup does not prevent multi-user chats, but these will have to go through
+a `Group`, which will be the recipient of the messages and then `Announce` them
+to the users in the `Group`.
diff --git a/docs/development/authentication_authorization.md b/docs/development/authentication_authorization.md
new file mode 100644
index 0000000..183bfc2
--- /dev/null
+++ b/docs/development/authentication_authorization.md
@@ -0,0 +1,21 @@
+# Authentication & Authorization
+
+## OAuth token-based authentication & authorization
+
+* Pleroma supports hierarchical OAuth scopes, just like Mastodon but with added granularity of admin scopes. For a reference, see [Mastodon OAuth scopes](https://docs.joinmastodon.org/api/oauth-scopes/).
+
+* It is important to either define OAuth scope restrictions or explicitly mark OAuth scope check as skipped, for every controller action. To define scopes, call `plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: [...]})`. To explicitly set OAuth scopes check skipped, call `plug(:skip_plug, Pleroma.Web.Plugs.OAuthScopesPlug <when ...>)`.
+
+* In controllers, `use Pleroma.Web, :controller` will result in `action/2` (see `Pleroma.Web.controller/0` for definition) be called prior to actual controller action, and it'll perform security / privacy checks before passing control to actual controller action.
+
+ For routes with `:authenticated_api` pipeline, authentication & authorization are expected, thus `OAuthScopesPlug` will be run unless explicitly skipped (also `EnsureAuthenticatedPlug` will be executed immediately before action even if there was an early run to give an early error, since `OAuthScopesPlug` supports `:proceed_unauthenticated` option, and other plugs may support similar options as well).
+
+ For `:api` pipeline routes, it'll be verified whether `OAuthScopesPlug` was called or explicitly skipped, and if it was not then auth information will be dropped for request. Then `EnsurePublicOrAuthenticatedPlug` will be called to ensure that either the instance is not private or user is authenticated (unless explicitly skipped). Such automated checks help to prevent human errors and result in higher security / privacy for users.
+
+## Non-OAuth authentication
+
+* With non-OAuth authentication ([HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) or HTTP header- or params-provided auth), OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways); auth plugs invoke `Pleroma.Helpers.AuthHelper.skip_oauth(conn)` in this case.
+
+## Auth-related configuration, OAuth consumer mode etc.
+
+See `Authentication` section of [the configuration cheatsheet](../configuration/cheatsheet.md#authentication).
diff --git a/docs/development/index.md b/docs/development/index.md
new file mode 100644
index 0000000..01a6175
--- /dev/null
+++ b/docs/development/index.md
@@ -0,0 +1 @@
+This section contains notes and guidelines for developers.
diff --git a/docs/development/setting_up_a_gitlab_runner.md b/docs/development/setting_up_a_gitlab_runner.md
new file mode 100644
index 0000000..88beb82
--- /dev/null
+++ b/docs/development/setting_up_a_gitlab_runner.md
@@ -0,0 +1,9 @@
+# Setting up a Gitlab-runner
+
+When you push changes, a pipeline will start some automated jobs. These are done with so called [runners](https://docs.gitlab.com/runner/), services that run somewhere on a server and run these automated jobs. These jobs typically run tests and should pass. If not, you probably need to fix something.
+
+Generally, Pleroma provides a runner, so you don't need to set up your own. However, if for whatever reason you want to set up your own, here's some high level instructions.
+
+1. We use docker to run the jobs, so you should install that. For Debian, you need to allow non-free packages in the [source list](https://wiki.debian.org/SourcesList). Then you can install docker with `apt install docker-compose`.
+2. You can [install](https://docs.gitlab.com/runner/install/index.html) and [configure](https://docs.gitlab.com/runner/register/index.html) a Gitlab-runner. It's probably easiest to install from the packages, but there are other options as well.
+3. When registering the runner, you'll need some values. You can find them in the project under your own name. Choose "Settings", "CI/CD", and then expand "Runners". For executor you can choose "docker". For default image, you can use the image used in <https://git.pleroma.social/pleroma/pleroma/-/blob/develop/.gitlab-ci.yml#L1> (although it shouldn't matter much).
diff --git a/docs/development/setting_up_pleroma_dev.md b/docs/development/setting_up_pleroma_dev.md
new file mode 100644
index 0000000..8da761d
--- /dev/null
+++ b/docs/development/setting_up_pleroma_dev.md
@@ -0,0 +1,70 @@
+# Setting up a Pleroma development environment
+
+Pleroma requires some adjustments from the defaults for running the instance locally. The following should help you to get started.
+
+## Installing
+
+1. Install Pleroma as explained in [the docs](../installation/debian_based_en.md), with some exceptions:
+ * You can use your own fork of the repository and add pleroma as a remote `git remote add pleroma 'https://git.pleroma.social/pleroma/pleroma'`
+ * You can skip systemd and nginx and all that stuff
+ * No need to create a dedicated pleroma user, it's easier to just use your own user
+ * For the DB you can still choose a dedicated user, the mix tasks set it up for you so it's no extra work for you
+ * For domain you can use `localhost`
+ * instead of creating a `prod.secret.exs`, create `dev.secret.exs`
+ * No need to prefix with `MIX_ENV=prod`. We're using dev and that's the default MIX_ENV
+2. Change the dev.secret.exs
+ * Change the scheme in `config :pleroma, Pleroma.Web.Endpoint` to http (see examples below)
+ * If you want to change other settings, you can do that too
+3. You can now start the server `mix phx.server`. Once it's build and started, you can access the instance on `http://<host>:<port>` (e.g.http://localhost:4000 ) and should be able to do everything locally you normaly can.
+
+Example config to change the scheme to http. Change the port if you want to run on another port.
+```elixir
+ config :pleroma, Pleroma.Web.Endpoint,
+ url: [host: "localhost", scheme: "http", port: 4000],
+```
+
+Example config to disable captcha. This makes it a bit easier to create test-users.
+```elixir
+config :pleroma, Pleroma.Captcha,
+ enabled: false
+```
+
+Example config to change the log level to info
+```elixir
+config :logger, :console,
+ # :debug :info :warning :error
+ level: :info
+```
+
+## Testing
+
+1. Create a `test.secret.exs` file with the content as shown below
+2. Create the database user and test database.
+ 1. You can use the `config/setup_db.psql` as a template. Copy the file if you want and change the database name, user and password to the values for the test-database (e.g. 'pleroma_local_test' for database and user). Then run this file like you did during installation.
+ 2. The tests will try to create the Database, so we'll have to allow our test-database user to create databases, `sudo -Hu postgres psql -c "ALTER USER pleroma_local_test WITH CREATEDB;"`
+3. Run the tests with `mix test`. The tests should succeed.
+
+Example content for the `test.secret.exs` file. Feel free to use another user, database name or password, just make sure the database is dedicated for the testing environment.
+```elixir
+# Pleroma test configuration
+
+# NOTE: This file should not be committed to a repo or otherwise made public
+# without removing sensitive information.
+
+import Config
+
+config :pleroma, Pleroma.Repo,
+ username: "pleroma_local_test",
+ password: "mysuperduperpassword",
+ database: "pleroma_local_test",
+ hostname: "localhost"
+
+```
+
+## Updating
+
+Update Pleroma as explained in [the docs](../administration/updating.md). Just make sure you pull from upstream and not from your own fork.
+
+## Working on multiple branches
+
+If you develop on a separate branch, it's possible you did migrations that aren't merged into another branch you're working on. If you have multiple things you're working on, it's probably best to set up multiple pleroma's each with their own database. If you finished with a branch and want to switch back to develop to start a new branch from there, you can drop the database and recreate the database (e.g. by using `config/setup_db.psql`). The commands to drop and recreate the database can be found in [the docs](../administration/backup.md).
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..3799a00
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,19 @@
+# Introduction to Pleroma
+## What is Pleroma?
+Pleroma is a federated social networking platform, compatible with Mastodon and other ActivityPub implementations. It is free software licensed under the AGPLv3.
+It actually consists of two components: a backend, named simply Pleroma, and a user-facing frontend, named Pleroma-FE. It also includes the Mastodon frontend, if that's your thing.
+It's part of what we call the fediverse, a federated network of instances which speak common protocols and can communicate with each other.
+One account on an instance is enough to talk to the entire fediverse!
+
+## How can I use it?
+
+Pleroma instances are already widely deployed, a list can be found at <https://the-federation.info/pleroma> and <https://fediverse.network/pleroma>.
+
+If you don't feel like joining an existing instance, but instead prefer to deploy your own instance, that's easy too!
+Installation instructions can be found in the installation section of these docs.
+
+## I got an account, now what?
+Great! Now you can explore the fediverse! Open the login page for your Pleroma instance (e.g. <https://pleroma.soykaf.com>) and login with your username and password. (If you don't have an account yet, click on Register)
+
+### Pleroma-FE
+The default front-end used by Pleroma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](../frontend). \ No newline at end of file
diff --git a/docs/installation/alpine_linux_en.md b/docs/installation/alpine_linux_en.md
new file mode 100644
index 0000000..c37ff0c
--- /dev/null
+++ b/docs/installation/alpine_linux_en.md
@@ -0,0 +1,228 @@
+# Installing on Alpine Linux
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
+## Installation
+
+This guide is a step-by-step installation guide for Alpine Linux. The instructions were verified against Alpine v3.10 standard image. You might miss additional dependencies if you use `netboot` instead.
+
+It assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-alpine-linux-on-your-linode/#configuration). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su -l <username> -s $SHELL -c 'command'` instead.
+
+{! backend/installation/generic_dependencies.include !}
+
+### Prepare the system
+
+* The community repository must be enabled in `/etc/apk/repositories`. Depending on which version and mirror you use this looks like `http://alpine.42.fr/v3.10/community`. If you autogenerated the mirror during installation:
+
+```shell
+awk 'NR==2' /etc/apk/repositories | sed 's/main/community/' | tee -a /etc/apk/repositories
+```
+
+* Then update the system, if not already done:
+
+```shell
+sudo apk update
+sudo apk upgrade
+```
+
+* Install some tools, which are needed later:
+
+```shell
+sudo apk add git build-base cmake file-dev
+```
+
+### Install Elixir and Erlang
+
+* Install Erlang and Elixir:
+
+```shell
+sudo apk add erlang erlang-runtime-tools erlang-xmerl elixir
+```
+
+* Install `erlang-eldap` if you want to enable ldap authenticator
+
+```shell
+sudo apk add erlang-eldap
+```
+
+### Install PostgreSQL
+
+* Install Postgresql server:
+
+```shell
+sudo apk add postgresql postgresql-contrib
+```
+
+* Initialize database:
+
+```shell
+sudo /etc/init.d/postgresql start
+```
+
+* Enable and start postgresql server:
+
+```shell
+sudo rc-update add postgresql
+```
+
+### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
+
+```shell
+sudo apk add ffmpeg imagemagick exiftool
+```
+
+### Install PleromaBE
+
+* Add a new system user for the Pleroma service:
+
+```shell
+sudo addgroup pleroma
+sudo adduser -S -s /bin/false -h /opt/pleroma -H -G pleroma pleroma
+```
+
+**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
+
+* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
+
+```shell
+sudo mkdir -p /opt/pleroma
+sudo chown -R pleroma:pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+```
+
+* Change to the new directory:
+
+```shell
+cd /opt/pleroma
+```
+
+* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
+
+```shell
+sudo -Hu pleroma mix deps.get
+```
+
+* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
+ * Answer with `yes` if it asks you to install `rebar3`.
+ * This may take some time, because parts of pleroma get compiled first.
+ * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
+
+* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+
+```shell
+sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
+```
+
+* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
+
+```shell
+sudo -Hu postgres psql -f config/setup_db.psql
+```
+
+* Now run the database migration:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
+```
+
+* Now you can start Pleroma already
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix phx.server
+```
+
+### Finalize installation
+
+If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create an OpenRC service file for Pleroma.
+
+#### Nginx
+
+* Install nginx, if not already done:
+
+```shell
+sudo apk add nginx
+```
+
+* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
+
+```shell
+sudo apk add certbot
+```
+
+and then set it up:
+
+```shell
+sudo mkdir -p /var/lib/letsencrypt/
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
+```
+
+If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
+
+* Copy the example nginx configuration to the nginx folder
+
+```shell
+sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf
+```
+
+* Before starting nginx edit the configuration and change it to your needs. You must change change `server_name` and the paths to the certificates. You can use `nano` (install with `apk add nano` if missing).
+
+```
+server {
+ server_name your.domain;
+ listen 80;
+ ...
+}
+
+server {
+ server_name your.domain;
+ listen 443 ssl http2;
+ ...
+ ssl_trusted_certificate /etc/letsencrypt/live/your.domain/chain.pem;
+ ssl_certificate /etc/letsencrypt/live/your.domain/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/your.domain/privkey.pem;
+ ...
+}
+```
+
+* Enable and start nginx:
+
+```shell
+sudo rc-update add nginx
+sudo service nginx start
+```
+
+If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
+
+```shell
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
+```
+
+#### OpenRC service
+
+* Copy example service file:
+
+```shell
+sudo cp /opt/pleroma/installation/init.d/pleroma /etc/init.d/pleroma
+```
+
+* Make sure to start it during the boot
+
+```shell
+sudo rc-update add pleroma
+```
+
+#### Create your first user
+
+If your instance is up and running, you can create your first user with administrative rights with the following task:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+
+#### Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/arch_linux_en.md b/docs/installation/arch_linux_en.md
new file mode 100644
index 0000000..285743d
--- /dev/null
+++ b/docs/installation/arch_linux_en.md
@@ -0,0 +1,221 @@
+# Installing on Arch Linux
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
+## Installation
+
+This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.archlinux.org/index.php/Sudo). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
+
+### Required packages
+
+* `postgresql`
+* `elixir`
+* `git`
+* `base-devel`
+* `cmake`
+* `file`
+
+#### Optional packages used in this guide
+
+* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
+* `certbot` (or any other ACME client for Let’s Encrypt certificates)
+* `ImageMagick`
+* `ffmpeg`
+* `exiftool`
+
+### Prepare the system
+
+* First update the system, if not already done:
+
+```shell
+sudo pacman -Syu
+```
+
+* Install some of the above mentioned programs:
+
+```shell
+sudo pacman -S git base-devel elixir cmake file
+```
+
+### Install PostgreSQL
+
+[Arch Wiki article](https://wiki.archlinux.org/index.php/PostgreSQL)
+
+* Install the `postgresql` package:
+
+```shell
+sudo pacman -S postgresql
+```
+
+* Initialize the database cluster:
+
+```shell
+sudo -iu postgres initdb -D /var/lib/postgres/data
+```
+
+* Start and enable the `postgresql.service`
+
+```shell
+sudo systemctl enable --now postgresql.service
+```
+
+### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
+
+```shell
+sudo pacman -S ffmpeg imagemagick perl-image-exiftool
+```
+
+### Install PleromaBE
+
+* Add a new system user for the Pleroma service:
+
+```shell
+sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
+```
+
+**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
+
+* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
+
+```shell
+sudo mkdir -p /opt/pleroma
+sudo chown -R pleroma:pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+```
+
+* Change to the new directory:
+
+```shell
+cd /opt/pleroma
+```
+
+* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
+
+```shell
+sudo -Hu pleroma mix deps.get
+```
+
+* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
+ * Answer with `yes` if it asks you to install `rebar3`.
+ * This may take some time, because parts of pleroma get compiled first.
+ * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
+
+* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+
+```shell
+sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
+```
+
+* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
+
+```shell
+sudo -Hu postgres psql -f config/setup_db.psql
+```
+
+* Now run the database migration:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
+```
+
+* Now you can start Pleroma already
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix phx.server
+```
+
+### Finalize installation
+
+If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create a systemd service file for Pleroma.
+
+#### Nginx
+
+* Install nginx, if not already done:
+
+```shell
+sudo pacman -S nginx
+```
+
+* Create directories for available and enabled sites:
+
+```shell
+sudo mkdir -p /etc/nginx/sites-{available,enabled}
+```
+
+* Append the following line at the end of the `http` block in `/etc/nginx/nginx.conf`:
+
+```Nginx
+include sites-enabled/*;
+```
+
+* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
+
+```shell
+sudo pacman -S certbot certbot-nginx
+```
+
+and then set it up:
+
+```shell
+sudo mkdir -p /var/lib/letsencrypt/
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
+```
+
+If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
+
+---
+
+* Copy the example nginx configuration and activate it:
+
+```shell
+sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx
+sudo ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
+```
+
+* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths)
+* Enable and start nginx:
+
+```shell
+sudo systemctl enable --now nginx.service
+```
+
+If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
+
+```shell
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
+```
+
+#### Other webserver/proxies
+
+You can find example configurations for them in `/opt/pleroma/installation/`.
+
+#### Systemd service
+
+* Copy example service file
+
+```shell
+sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
+```
+
+* Edit the service file and make sure that all paths fit your installation
+* Enable and start `pleroma.service`:
+
+```shell
+sudo systemctl enable --now pleroma.service
+```
+
+#### Create your first user
+
+If your instance is up and running, you can create your first user with administrative rights with the following task:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+
+#### Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md
new file mode 100644
index 0000000..4e52b21
--- /dev/null
+++ b/docs/installation/debian_based_en.md
@@ -0,0 +1,184 @@
+# Installing on Debian Based Distributions
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
+## Installation
+
+This guide will assume you are on Debian 11 (“bullseye”) or later. This guide should also work with Ubuntu 18.04 (“Bionic Beaver”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
+
+{! backend/installation/generic_dependencies.include !}
+
+### Prepare the system
+
+* First update the system, if not already done:
+
+```shell
+sudo apt update
+sudo apt full-upgrade
+```
+
+* Install some of the above mentioned programs:
+
+```shell
+sudo apt install git build-essential postgresql postgresql-contrib cmake libmagic-dev
+```
+
+### Install Elixir and Erlang
+
+* Install Elixir and Erlang (you might need to use backports or [asdf](https://github.com/asdf-vm/asdf) on old systems):
+
+```shell
+sudo apt update
+sudo apt install elixir erlang-dev erlang-nox
+```
+
+
+### Optional packages: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)
+
+```shell
+sudo apt install imagemagick ffmpeg libimage-exiftool-perl
+```
+
+### Install PleromaBE
+
+* Add a new system user for the Pleroma service:
+
+```shell
+sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
+```
+
+**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
+
+* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
+
+```shell
+sudo mkdir -p /opt/pleroma
+sudo chown -R pleroma:pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+```
+
+* Change to the new directory:
+
+```shell
+cd /opt/pleroma
+```
+
+* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
+
+```shell
+sudo -Hu pleroma mix deps.get
+```
+
+* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
+ * Answer with `yes` if it asks you to install `rebar3`.
+ * This may take some time, because parts of pleroma get compiled first.
+ * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
+
+* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+
+```shell
+sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
+```
+
+
+* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
+
+```shell
+sudo -Hu postgres psql -f config/setup_db.psql
+```
+
+* Now run the database migration:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
+```
+
+* Now you can start Pleroma already
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix phx.server
+```
+
+### Finalize installation
+
+If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create a systemd service file for Pleroma.
+
+#### Nginx
+
+* Install nginx, if not already done:
+
+```shell
+sudo apt install nginx
+```
+
+* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
+
+```shell
+sudo apt install certbot
+```
+
+and then set it up:
+
+```shell
+sudo mkdir -p /var/lib/letsencrypt/
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
+```
+
+If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
+
+---
+
+* Copy the example nginx configuration and activate it:
+
+```shell
+sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx
+sudo ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
+```
+
+* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths)
+* Enable and start nginx:
+
+```shell
+sudo systemctl enable --now nginx.service
+```
+
+If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
+
+```shell
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
+```
+
+#### Other webserver/proxies
+
+You can find example configurations for them in `/opt/pleroma/installation/`.
+
+#### Systemd service
+
+* Copy example service file
+
+```shell
+sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
+```
+
+* Edit the service file and make sure that all paths fit your installation
+* Enable and start `pleroma.service`:
+
+```shell
+sudo systemctl enable --now pleroma.service
+```
+
+#### Create your first user
+
+If your instance is up and running, you can create your first user with administrative rights with the following task:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+
+#### Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md
new file mode 100644
index 0000000..3736e85
--- /dev/null
+++ b/docs/installation/debian_based_jp.md
@@ -0,0 +1,195 @@
+# Pleromaの入れ方
+## 日本語訳について
+
+この記事は [Installing on Debian based distributions](Installing on Debian based distributions) の日本語訳です。何かがおかしいと思ったら、原文を見てください。
+
+## インストール
+
+このガイドはDebian Stretchを利用することを想定しています。Ubuntu 16.04や18.04でもおそらく動作します。また、ユーザはrootもしくはsudoにより管理者権限を持っていることを前提とします。もし、以下の操作をrootユーザで行う場合は、 `sudo` を無視してください。ただし、`sudo -Hu pleroma` のようにユーザを指定している場合には `su <username> -s $SHELL -c 'command'` を代わりに使ってください。
+
+### 必要なソフトウェア
+
+- PostgreSQL 9.6以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
+- `postgresql-contrib` 9.6以上 (同上)
+- Elixir 1.8 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
+- `erlang-dev`
+- `erlang-nox`
+- `git`
+- `build-essential`
+- `cmake`
+- `libmagic-dev`
+
+#### このガイドで利用している追加パッケージ
+
+- `nginx` (おすすめです。他のリバースプロキシを使う場合は、参考となる設定をこのリポジトリから探してください)
+- `certbot` (または何らかのLet's Encrypt向けACMEクライアント)
+- `ImageMagick`
+- `ffmpeg`
+- `exiftool`
+
+### システムを準備する
+
+* まずシステムをアップデートしてください。
+```
+sudo apt update
+sudo apt full-upgrade
+```
+
+* 上記に挙げたパッケージをインストールしておきます。
+```
+sudo apt install git build-essential postgresql postgresql-contrib cmake ffmpeg imagemagick libmagic-dev
+```
+
+### ElixirとErlangをインストールします
+
+* Erlangのリポジトリをダウンロードおよびインストールします。
+```
+wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
+sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
+```
+
+* ElixirとErlangをインストールします、
+```
+sudo apt update
+sudo apt install elixir erlang-dev erlang-nox
+```
+
+### オプションパッケージ: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)
+
+```shell
+sudo apt install imagemagick ffmpeg libimage-exiftool-perl
+```
+
+### Pleroma BE (バックエンド) をインストールします
+
+* Pleroma用に新しいユーザーを作ります。
+
+```
+sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
+```
+
+**注意**: Pleromaユーザとして単発のコマンドを実行したい場合はは、`sudo -Hu pleroma command` を使ってください。シェルを使いたい場合は `sudo -Hu pleroma $SHELL`です。もし `sudo` を使わない場合は、rootユーザで `su -l pleroma -s $SHELL -c 'command'` とすることでコマンドを、`su -l pleroma -s $SHELL` とすることでシェルを開始できます。
+
+* Gitリポジトリをクローンします。
+```
+sudo mkdir -p /opt/pleroma
+sudo chown -R pleroma:pleroma /opt/pleroma
+sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
+```
+
+* 新しいディレクトリに移動します。
+```
+cd /opt/pleroma
+```
+
+* Pleromaが依存するパッケージをインストールします。Hexをインストールしてもよいか聞かれたら、yesを入力してください。
+```
+sudo -Hu pleroma mix deps.get
+```
+
+* コンフィギュレーションを生成します。
+```
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen
+```
+ * rebar3をインストールしてもよいか聞かれたら、yesを入力してください。
+ * このときにpleromaの一部がコンパイルされるため、この処理には時間がかかります。
+ * あなたのインスタンスについて、いくつかの質問されます。この質問により `config/generated_config.exs` という設定ファイルが生成されます。
+
+
+* コンフィギュレーションを確認して、もし問題なければ、ファイル名を変更してください。
+```
+sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
+```
+
+* 先程のコマンドで、すでに `config/setup_db.psql` というファイルが作られています。このファイルをもとに、データベースを作成します。
+```
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen
+```
+
+* そして、データベースのマイグレーションを実行します。
+```
+sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
+```
+
+* これでPleromaを起動できるようになりました。
+```
+sudo -Hu pleroma MIX_ENV=prod mix phx.server
+```
+
+### インストールの最終段階
+
+あなたの新しいインスタンスを世界に向けて公開するには、nginx等のWebサーバやプロキシサーバをPleromaの前段に使用する必要があります。また、Pleroma のためにシステムサービスファイルを作成する必要があります。
+
+#### Nginx
+
+* まだインストールしていないなら、nginxをインストールします。
+```
+sudo apt install nginx
+```
+
+* SSLをセットアップします。他の方法でもよいですが、ここではcertbotを説明します。
+certbotを使うならば、まずそれをインストールします。
+```
+sudo apt install certbot
+```
+そしてセットアップします。
+```
+sudo mkdir -p /var/lib/letsencrypt/
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
+```
+もしうまくいかないときは、nginxが正しく動いていない可能性があります。先にnginxを設定してください。ssl "on" を "off" に変えてから再試行してください。
+
+---
+
+* nginxの設定ファイルサンプルをnginxフォルダーにコピーします。
+```
+sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx
+sudo ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
+```
+
+* nginxを起動する前に、設定ファイルを編集してください。例えば、サーバー名、証明書のパスなどを変更する必要があります。
+* nginxを再起動します。
+```
+sudo systemctl enable --now nginx.service
+```
+
+もし証明書を更新する必要が出てきた場合には、nginxの関連するlocationブロックのコメントアウトを外し、以下のコマンドを動かします。
+
+```
+sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
+```
+
+#### 他のWebサーバやプロキシ
+これに関してはサンプルが `/opt/pleroma/installation/` にあるので、探してみてください。
+
+#### Systemd サービス
+
+* サービスファイルのサンプルをコピーします。
+```
+sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
+```
+
+* サービスファイルを変更します。すべてのパスが正しいことを確認してください
+* サービスを有効化し `pleroma.service` を開始してください
+```
+sudo systemctl enable --now pleroma.service
+```
+
+#### 初期ユーザの作成
+
+新たにインスタンスを作成したら、以下のコマンドにより管理者権限を持った初期ユーザを作成できます。
+
+```
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+
+#### その他の設定とカスタマイズ
+
+{! backend/installation/further_reading.include !}
+
+## 質問ある?
+
+インストールについて質問がある、もしくは、うまくいかないときは、以下のところで質問できます。
+
+* [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat)
+* **libera.chat** の **#pleroma** IRCチャンネル
diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md
new file mode 100644
index 0000000..9cbe0f2
--- /dev/null
+++ b/docs/installation/freebsd_en.md
@@ -0,0 +1,218 @@
+# Installing on FreeBSD
+
+This document was written for FreeBSD 12.1, but should be work on future releases.
+
+{! backend/installation/generic_dependencies.include !}
+
+## Installing software used in this guide
+
+This assumes the target system has `pkg(8)`.
+
+```
+# pkg install elixir postgresql12-server postgresql12-client postgresql12-contrib git-lite sudo nginx gmake acme.sh cmake
+```
+
+Copy the rc.d scripts to the right directory:
+
+Setup the required services to automatically start at boot, using `sysrc(8)`.
+
+```
+# sysrc nginx_enable=YES
+# sysrc postgresql_enable=YES
+```
+
+## Initialize postgres
+
+```
+# service postgresql initdb
+# service postgresql start
+```
+
+### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
+
+```shell
+# pkg install imagemagick ffmpeg p5-Image-ExifTool
+```
+
+## Configuring Pleroma
+
+Create a user for Pleroma:
+
+```
+# pw add user pleroma -m
+# echo 'export LC_ALL="en_US.UTF-8"' >> /home/pleroma/.profile
+# su -l pleroma
+```
+
+Clone the repository:
+
+```
+$ cd $HOME # Should be the same as /home/pleroma
+$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git
+```
+
+Configure Pleroma. Note that you need a domain name at this point:
+
+```
+$ cd /home/pleroma/pleroma
+$ mix deps.get # Enter "y" when asked to install Hex
+$ MIX_ENV=prod mix pleroma.instance gen # You will be asked a few questions here.
+$ cp config/generated_config.exs config/prod.secret.exs
+```
+
+Since Postgres is configured, we can now initialize the database. There should
+now be a file in `config/setup_db.psql` that makes this easier. Edit it, and
+*change the password* to a password of your choice. Make sure it is secure, since
+it'll be protecting your database. As root, you can now initialize the database:
+
+```
+# cd /home/pleroma/pleroma
+# sudo -Hu postgres -g postgres psql -f config/setup_db.psql
+```
+
+Postgres allows connections from all users without a password by default. To
+fix this, edit `/var/db/postgres/data12/pg_hba.conf`. Change every `trust` to
+`password`.
+
+Once this is done, restart Postgres with:
+```
+# service postgresql restart
+```
+
+Run the database migrations.
+
+Back as the pleroma user, run the following to implement any database migrations.
+
+```
+# su -l pleroma
+$ cd /home/pleroma/pleroma
+$ MIX_ENV=prod mix ecto.migrate
+```
+
+You will need to do this whenever you update with `git pull`:
+
+## Configuring acme.sh
+
+We'll be using acme.sh in Stateless Mode for TLS certificate renewal.
+
+First, as root, allow the user `acme` to have access to the acme log file, as follows:
+
+```
+# touch /var/log/acme.sh.log
+# chown acme:acme /var/log/acme.sh.log
+# chmod 600 /var/log/acme.sh.log
+```
+
+Next, obtain your account fingerprint:
+
+```
+# sudo -Hu acme -g acme acme.sh --register-account
+```
+
+You need to add the following to your nginx configuration for the server
+running on port 80:
+
+```
+ location ~ ^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)$ {
+ default_type text/plain;
+ return 200 "$1.6fXAG9VyG0IahirPEU2ZerUtItW2DHzDzD9wZaEKpqd";
+ }
+```
+
+Replace the string after after `$1.` with your fingerprint.
+
+Start nginx:
+
+```
+# service nginx start
+```
+
+It should now be possible to issue a cert (replace `example.com`
+with your domain name):
+
+```
+# sudo -Hu acme -g acme acme.sh --issue -d example.com --stateless
+```
+
+Let's add auto-renewal to `/etc/crontab`
+(replace `example.com` with your domain):
+
+```
+/usr/local/bin/sudo -Hu acme -g acme /usr/local/sbin/acme.sh -r -d example.com --stateless
+```
+
+### Configuring nginx
+
+FreeBSD's default nginx configuration does not contain an include directive, which is
+typically used for multiple sites. Therefore, you will need to first create the required
+directory as follows:
+
+
+```
+# mkdir -p /usr/local/etc/nginx/sites-available
+```
+
+Next, add an `include` directive to `/usr/local/etc/nginx/nginx.conf`, within the `http {}`
+block, as follows:
+
+
+```
+http {
+...
+ include /usr/local/etc/nginx/sites-available/*;
+}
+```
+
+As root, copy `/home/pleroma/pleroma/installation/pleroma.nginx` to
+`/usr/local/etc/nginx/sites-available/pleroma.nginx`.
+
+Edit the defaults of `/usr/local/etc/nginx/sites-available/pleroma.nginx`:
+
+* Change `ssl_trusted_certificate` to `/var/db/acme/certs/example.tld/example.tld.cer`.
+* Change `ssl_certificate` to `/var/db/acme/certs/example.tld/fullchain.cer`.
+* Change `ssl_certificate_key` to `/var/db/acme/certs/example.tld/example.tld.key`.
+* Change all references of `example.tld` to your instance's domain name.
+
+## Creating a startup script for Pleroma
+
+Pleroma will need to compile when it initially starts, which typically takes a longer
+period of time. Therefore, it is good practice to initially run pleroma from the
+command-line before utilizing the rc.d script. That is done as follows:
+
+```
+# su -l pleroma
+$ cd $HOME/pleroma
+$ MIX_ENV=prod mix phx.server
+```
+
+Copy the startup script to the correct location and make sure it's executable:
+
+```
+# cp /home/pleroma/pleroma/installation/freebsd/rc.d/pleroma /usr/local/etc/rc.d/pleroma
+# chmod +x /usr/local/etc/rc.d/pleroma
+```
+
+Update the `/etc/rc.conf` and start pleroma with the following commands:
+
+```
+# sysrc pleroma_enable=YES
+# service pleroma start
+```
+
+#### Create your first user
+
+If your instance is up and running, you can create your first user with administrative rights with the following task:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+## Conclusion
+
+Restart nginx with `# service nginx restart` and you should be up and running.
+
+Make sure your time is in sync, or other instances will receive your posts with
+incorrect timestamps. You should have ntpd running.
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/further_reading.include b/docs/installation/further_reading.include
new file mode 100644
index 0000000..46752c7
--- /dev/null
+++ b/docs/installation/further_reading.include
@@ -0,0 +1,5 @@
+* [How Federation Works/Why is my Federated Timeline empty?](https://blog.soykaf.com/post/how-federation-works/)
+* [Backup your instance](../administration/backup.md)
+* [Updating your instance](../administration/updating.md)
+* [Hardening your instance](../configuration/hardening.md)
+* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include
new file mode 100644
index 0000000..dcaacfd
--- /dev/null
+++ b/docs/installation/generic_dependencies.include
@@ -0,0 +1,16 @@
+## Required dependencies
+
+* PostgreSQL 9.6+
+* Elixir 1.10+
+* Erlang OTP 22.2+
+* git
+* file / libmagic
+* gcc (clang might also work)
+* GNU make
+* CMake
+
+## Optional dependencies
+
+* ImageMagick
+* FFmpeg
+* exiftool
diff --git a/docs/installation/gentoo_en.md b/docs/installation/gentoo_en.md
new file mode 100644
index 0000000..36882c8
--- /dev/null
+++ b/docs/installation/gentoo_en.md
@@ -0,0 +1,302 @@
+# Installing on Gentoo GNU/Linux
+
+{! backend/installation/otp_vs_from_source_source.include !}
+
+## Installation
+
+This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.gentoo.org/wiki/Sudo). Lines that begin with `#` indicate that they should be run as the superuser. Lines using `$` should be run as the indicated user, e.g. `pleroma$` should be run as the `pleroma` user.
+
+{! backend/installation/generic_dependencies.include !}
+
+### Your make.conf, package.use, and USE flags
+
+The only specific USE flag you should need is the `uuid` flag for `dev-db/postgresql`. Add the following line to any new file in `/etc/portage/package.use`. If you would like a suggested name for the file, either `postgresql` or `pleroma` would do fine, depending on how you like to arrange your package.use flags.
+
+```text
+dev-db/postgresql uuid
+```
+
+You could opt to add `USE="uuid"` to `/etc/portage/make.conf` if you'd rather set this as a global USE flags, but this flags does unrelated things in other packages, so keep that in mind if you elect to do so.
+
+Double check your compiler flags in `/etc/portage/make.conf`. If you require any special compilation flags or would like to set up remote builds, now is the time to do so. Be sure that your CFLAGS and MAKEOPTS make sense for the platform you are using. It is not recommended to use above `-O2` or risky optimization flags for a production server.
+
+### Installing a cron daemon
+
+Gentoo quite pointedly does not come with a cron daemon installed, and as such it is recommended you install one to automate certbot renewals and to allow other system administration tasks to be run automatically. Gentoo has [a whole wide world of cron options](https://wiki.gentoo.org/wiki/Cron) but if you just want A Cron That Works, `emerge --ask virtual/cron` will install the default cron implementation (probably cronie) which will work just fine. For the purpouses of this guide, we will be doing just that.
+
+### Required ebuilds
+
+* `dev-db/postgresql`
+* `dev-lang/elixir`
+* `dev-vcs/git`
+* `dev-util/cmake`
+* `sys-apps/file`
+
+#### Optional ebuilds used in this guide
+
+* `www-servers/nginx` (preferred, example configs for other reverse proxies can be found in the repo)
+* `app-crypt/certbot` (or any other ACME client for Let’s Encrypt certificates)
+* `app-crypt/certbot-nginx` (nginx certbot plugin that allows use of the all-powerful `--nginx` flag on certbot)
+* `media-gfx/imagemagick`
+* `media-video/ffmpeg`
+* `media-libs/exiftool`
+
+### Prepare the system
+
+* First ensure that you have the latest copy of the portage ebuilds if you have not synced them yet:
+
+```shell
+ # emaint sync -a
+```
+
+* Emerge all required the required and suggested software in one go:
+
+```shell
+ # emerge --ask dev-db/postgresql dev-lang/elixir dev-vcs/git www-servers/nginx app-crypt/certbot app-crypt/certbot-nginx dev-util/cmake sys-apps/file
+```
+
+If you would not like to install the optional packages, remove them from this line.
+
+If you're running this from a low-powered virtual machine, it should work though it will take some time. There were no issues on a VPS with a single core and 1GB of RAM; if you are using an even more limited device and run into issues, you can try creating a swapfile or use a more powerful machine running Gentoo to [cross build](https://wiki.gentoo.org/wiki/Cross_build_environment). If you have a wait ahead of you, now would be a good time to take a break, strech a bit, refresh your beverage of choice and/or get a snack, and reply to Arch users' posts with "I use Gentoo btw" as we do.
+
+### Install PostgreSQL
+
+[Gentoo Wiki article](https://wiki.gentoo.org/wiki/PostgreSQL) as well as [PostgreSQL QuickStart](https://wiki.gentoo.org/wiki/PostgreSQL/QuickStart) might be worth a quick glance, as the way Gentoo handles postgres is slightly unusual, with built in capability to have two different databases running for testing and live or whatever other purpouse. While it is still straightforward to install, it does mean that the version numbers used in this guide might change for future updates, so keep an eye out for the output you get from `emerge` to ensure you are using the correct ones.
+
+* Install postgresql if you have not done so already:
+
+```shell
+ # emerge --ask dev-db/postgresql
+```
+
+Ensure that `/etc/conf.d/postgresql-11` has the encoding you want (it defaults to UTF8 which is probably what you want) and make any adjustments to the data directory if you find it necessary. Be sure to adjust the number at the end depending on what version of postgres you actually installed.
+
+* Initialize the database cluster
+
+The output from emerging postgresql should give you a command for initializing the postgres database. The default slot should be indicated in this command, ensure that it matches the command below.
+
+```shell
+ # emerge --config dev-db/postgresql:11
+```
+
+* Start postgres and enable the system service
+
+```shell
+ # /etc/init.d/postgresql-11 start
+ # rc-update add postgresql-11 default
+ ```
+
+### A note on licenses, the AGPL, and deployment procedures
+
+If you do not plan to make any modifications to your Pleroma instance, cloning directly from the main repo will get you what you need. However, if you plan on doing any contributions to upstream development, making changes or modifications to your instance, making custom themes, or want to play around--and let's be honest here, if you're using Gentoo that is most likely you--you will save yourself a lot of headache later if you take the time right now to fork the Pleroma repo and use that in the following section.
+
+Not only does this make it much easier to deploy changes you make, as you can commit and pull from upstream and all that good stuff from the comfort of your local machine then simply `git pull` on your instance server when you're ready to deploy, it also ensures you are compliant with the Affero General Public Licence that Pleroma is licenced under, which stipulates that all network services provided with modified AGPL code must publish their changes on a publicly available internet service and for free. It also makes it much easier to ask for help from and provide help to your fellow Pleroma admins if your public repo always reflects what you are running because it is part of your deployment procedure.
+
+### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md))
+
+```shell
+# emerge --ask media-video/ffmpeg media-gfx/imagemagick media-libs/exiftool
+```
+
+### Install PleromaBE
+
+* Add a new system user for the Pleroma service and set up default directories:
+
+Remove `,wheel` if you do not want this user to be able to use `sudo`, however note that being able to `sudo` as the `pleroma` user will make finishing the insallation and common maintenence tasks somewhat easier:
+
+```shell
+ # useradd -m -G users,wheel -s /bin/bash pleroma
+```
+
+Optional: If you are using sudo, review your sudo setup to ensure it works for you. The `/etc/sudoers` file has a lot of options and examples to help you, and [the Gentoo sudo guide](https://wiki.gentoo.org/wiki/Sudo) has more information. Finishing this installation will be somewhat easier if you have a way to sudo from the `pleroma` user, but it might be best to not allow that user to sudo during normal operation, and as such there will be a reminder at the end of this guide to double check if you would like to lock down the `pleroma` user after initial setup.
+
+**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don't have or want `sudo` or would like to use the system as the `pleroma` user for instance maintenance tasks, you can simply use `su - pleroma` to switch to the `pleroma` user.
+
+* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
+
+It is highly recommended you use your own fork for the `https://path/to/repo` part below, however if you foolishly decide to forego using your own fork, the primary repo `https://git.pleroma.social/pleroma/pleroma` will work here.
+
+```shell
+ pleroma$ cd ~
+ pleroma$ git clone -b stable https://path/to/repo
+```
+
+* Change to the new directory:
+
+```shell
+pleroma$ cd ~/pleroma
+```
+
+* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
+
+```shell
+pleroma$ mix deps.get
+```
+
+* Generate the configuration:
+
+```shell
+pleroma$ MIX_ENV=prod mix pleroma.instance gen
+```
+
+ * Answer with `yes` if it asks you to install `rebar3`.
+
+ * This part precompiles some parts of Pleroma, so it might take a few moments
+
+ * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
+
+ * Spend some time with `generated_config.exs` to ensure that everything is in order. If you plan on using an S3-compatible service to store your local media, that can be done here. You will likely mostly be using `prod.secret.exs` for a production instance, however if you would like to set up a development environment, make a copy to `dev.secret.exs` and adjust settings as needed as well.
+
+```shell
+pleroma$ mv config/generated_config.exs config/prod.secret.exs
+```
+
+* The previous command creates also the file `config/setup_db.psql`, with which you can create the database. Ensure that it is using the correct database name on the `CREATE DATABASE` and the `\c` lines, then run the postgres script:
+
+```shell
+pleroma$ sudo -Hu postgres psql -f config/setup_db.psql
+```
+
+* Now run the database migration:
+
+```shell
+pleroma$ MIX_ENV=prod mix ecto.migrate
+```
+
+* Now you can start Pleroma already
+
+```shell
+pleroma$ MIX_ENV=prod mix phx.server
+```
+
+It probably won't work over the public internet quite yet, however, as we still need to set up a web servere to proxy to the pleroma application, as well as configure SSL.
+
+### Finalize installation
+
+Assuming you want to open your newly installed federated social network to, well, the federation, you should run nginx or some other webserver/proxy in front of Pleroma. It is also a good idea to set up Pleroma to run as a system service.
+
+#### Nginx
+
+* Install nginx, if not already done:
+
+```shell
+ # emerge --ask www-servers/nginx
+```
+
+* Create directories for available and enabled sites:
+
+```shell
+ # mkdir -p /etc/nginx/sites-{available,enabled}
+```
+
+* Append the following line at the end of the `http` block in `/etc/nginx/nginx.conf`:
+
+```Nginx
+include sites-enabled/*;
+```
+
+* Setup your SSL cert, using your method of choice or certbot. If using certbot, install it if you haven't already:
+
+```shell
+ # emerge --ask app-crypt/certbot app-crypt/certbot-nginx
+```
+
+and then set it up:
+
+```shell
+ # mkdir -p /var/lib/letsencrypt/
+ # certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
+```
+
+If that doesn't work the first time, add `--dry-run` to further attempts to avoid being ratelimited as you identify the issue, and do not remove it until the dry run succeeds. If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again). Often the answer to issues with certbot is to use the `--nginx` flag once you have nginx up and running.
+
+If you are using any additional subdomains, such as for a media proxy, you can re-run the same command with the subdomain in question. When it comes time to renew later, you will not need to run multiple times for each domain, one renew will handle it.
+
+---
+
+* Copy the example nginx configuration and activate it:
+
+```shell
+ # cp /home/pleroma/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/
+ # ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
+```
+
+* Take some time to ensure that your nginx config is correct
+
+Replace all instances of `example.tld` with your instance's public URL. If for whatever reason you made changes to the port that your pleroma app runs on, be sure that is reflected in your configuration.
+
+Pay special attention to the line that begins with `ssl_ecdh_curve`. It is stongly advised to comment that line out so that OpenSSL will use its full capabilities, and it is also possible you are running OpenSSL 1.0.2 necessitating that you do this.
+
+* Enable and start nginx:
+
+```shell
+ # rc-update add nginx default
+ # /etc/init.d/nginx start
+```
+
+If you are using certbot, it is HIGHLY recommend you set up a cron job that renews your certificate, and that you install the suggested `certbot-nginx` plugin. If you don't do these things, you only have yourself to blame when your instance breaks suddenly because you forgot about it.
+
+First, ensure that the command you will be installing into your crontab works.
+
+```shell
+ # /usr/bin/certbot renew --nginx
+```
+
+Assuming not much time has passed since you got certbot working a few steps ago, you should get a message for all domains you installed certificates for saying `Cert not yet due for renewal`.
+
+Now, run crontab as a superuser with `crontab -e` or `sudo crontab -e` as appropriate, and add the following line to your cron:
+
+```cron
+0 0 1 * * /usr/bin/certbot renew --nginx
+```
+
+This will run certbot on the first of the month at midnight. If you'd rather run more frequently, it's not a bad idea, feel free to go for it.
+
+#### Other webserver/proxies
+
+If you would like to use other webservers or proxies, there are example configurations for some popular alternatives in `/home/pleroma/pleroma/installation/`. You can, of course, check out [the Gentoo wiki](https://wiki.gentoo.org) for more information on installing and configuring said alternatives.
+
+#### Create the uploads folder
+
+Even if you are using S3, Pleroma needs someplace to store media posted on your instance. If you are using the `/home/pleroma/pleroma` root folder suggested by this guide, simply:
+
+```shell
+ pleroma$ mkdir -p ~/pleroma/uploads
+ ```
+
+#### init.d service
+
+* Copy example service file
+
+```shell
+ # cp /home/pleroma/pleroma/installation/init.d/pleroma /etc/init.d/
+```
+
+* Be sure to take a look at this service file and make sure that all paths fit your installation
+
+* Enable and start `pleroma`:
+
+```shell
+ # rc-update add pleroma default
+ # /etc/init.d/pleroma start
+```
+
+#### Create your first user
+
+If your instance is up and running, you can create your first user with administrative rights with the following task:
+
+```shell
+pleroma$ MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+
+#### Privilege cleanup
+
+If you opted to allow sudo for the `pleroma` user but would like to remove the ability for greater security, now might be a good time to edit `/etc/sudoers` and/or change the groups the `pleroma` user belongs to. Be sure to restart the pleroma service afterwards to ensure it picks up on the changes.
+
+#### Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/migrating_from_source_otp_en.md b/docs/installation/migrating_from_source_otp_en.md
new file mode 100644
index 0000000..f6f2340
--- /dev/null
+++ b/docs/installation/migrating_from_source_otp_en.md
@@ -0,0 +1,143 @@
+# Switching a from-source install to OTP releases
+
+{! backend/installation/otp_vs_from_source.include !}
+
+In this guide we cover how you can migrate from a from source installation to one using OTP releases.
+
+## Pre-requisites
+You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo su`/`su`.
+
+The system needs to have `curl` and `unzip` installed for downloading and unpacking release builds.
+
+=== "Alpine"
+ ```sh
+ apk add curl unzip
+ ```
+
+=== "Debian/Ubuntu"
+ ```sh
+ apt install curl unzip
+ ```
+
+## Moving content out of the application directory
+When using OTP releases the application directory changes with every version so it would be a bother to keep content there (and also dangerous unless `--no-rm` option is used when updating). Fortunately almost all paths in Pleroma are configurable, so it is possible to move them out of there.
+
+Pleroma should be stopped before proceeding.
+
+### Moving uploads/custom public files directory
+
+```sh
+# Create uploads directory and set proper permissions (skip if using a remote uploader)
+# Note: It does not have to be `/var/lib/pleroma/uploads`, you can configure it to be something else later
+mkdir -p /var/lib/pleroma/uploads
+chown -R pleroma /var/lib/pleroma
+
+# Create custom public files directory
+# Note: It does not have to be `/var/lib/pleroma/static`, you can configure it to be something else later
+mkdir -p /var/lib/pleroma/static
+chown -R pleroma /var/lib/pleroma
+
+# If you use the local uploader with default settings your uploads should be located in `~pleroma/uploads`
+mv ~pleroma/uploads/* /var/lib/pleroma/uploads
+
+# If you have created the custom public files directory with default settings it should be located in `~pleroma/instance/static`
+mv ~pleroma/instance/static /var/lib/pleroma/static
+```
+
+### Moving emoji
+Assuming you have all emojis in subdirectories of `priv/static/emoji` moving them can be done with
+```sh
+mkdir /var/lib/pleroma/static/emoji
+ls -d ~pleroma/priv/static/emoji/*/ | xargs -i sh -c 'mv "{}" "/var/lib/pleroma/static/emoji/$(basename {})"'
+```
+
+But, if for some reason you have custom emojis in the root directory you should copy the whole directory instead.
+```sh
+mv ~pleroma/priv/static/emoji /var/lib/pleroma/static/emoji
+```
+and then copy custom emojis to `/var/lib/pleroma/static/emoji/custom`.
+
+This is needed because storing custom emojis in the root directory is deprecated, but if you just move them to `/var/lib/pleroma/static/emoji/custom` it will break emoji urls on old posts.
+
+Note that globs have been replaced with `pack_extensions`, so if your emojis are not in png/gif you should [modify the default value](../configuration/cheatsheet.md#emoji).
+
+### Moving the config
+```sh
+# Create the config directory
+# The default path for Pleroma config is /etc/pleroma/config.exs
+# but it can be set via PLEROMA_CONFIG_PATH environment variable
+mkdir -p /etc/pleroma
+
+# Move the config file
+mv ~pleroma/config/prod.secret.exs /etc/pleroma/config.exs
+
+# Change `use Mix.Config` at the top to `import Config`
+$EDITOR /etc/pleroma/config.exs
+```
+## Installing the release
+Before proceeding, get the flavour from [Detecting flavour](otp_en.md#detecting-flavour) section in OTP installation guide.
+```sh
+# Delete all files in pleroma user's directory
+rm -r ~pleroma/*
+
+# Set the flavour environment variable to the string you got in Detecting flavour section.
+# For example if the flavour is `amd64-musl` the command will be
+export FLAVOUR="amd64-musl"
+
+# Clone the release build into a temporary directory and unpack it
+# Replace `stable` with `unstable` if you want to run the unstable branch
+su pleroma -s $SHELL -lc "
+curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
+unzip /tmp/pleroma.zip -d /tmp/
+"
+
+# Move the release to the home directory and delete temporary files
+su pleroma -s $SHELL -lc "
+mv /tmp/release/* ~pleroma/
+rmdir /tmp/release
+rm /tmp/pleroma.zip
+"
+
+# Start the instance to verify that everything is working as expected
+su pleroma -s $SHELL -lc "./bin/pleroma daemon"
+
+# Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly
+sleep 20 && curl http://localhost:4000/api/v1/instance
+
+# Stop the instance
+su pleroma -s $SHELL -lc "./bin/pleroma stop"
+```
+
+## Setting up a system service
+OTP releases have different service files than from-source installs so they need to be copied over again.
+
+**Warning:** The service files assume pleroma user's home directory is `/opt/pleroma`, please make sure all paths fit your installation.
+
+=== "Alpine"
+ ```sh
+ # Copy the service into a proper directory
+ cp -f ~pleroma/installation/init.d/pleroma /etc/init.d/pleroma
+
+ # Start pleroma
+ rc-service pleroma start
+ ```
+
+=== "Debian/Ubuntu"
+ ```sh
+ # Copy the service into a proper directory
+ cp ~pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
+
+ # Reload service files
+ systemctl daemon-reload
+
+ # Reenable pleroma to start on boot
+ systemctl reenable pleroma
+
+ # Start pleroma
+ systemctl start pleroma
+ ```
+
+## Running mix tasks
+Refer to [Running mix tasks](otp_en.md#running-mix-tasks) section from OTP release installation guide.
+## Updating
+Refer to [Updating](otp_en.md#updating) section from OTP release installation guide.
diff --git a/docs/installation/netbsd_en.md b/docs/installation/netbsd_en.md
new file mode 100644
index 0000000..41b3b00
--- /dev/null
+++ b/docs/installation/netbsd_en.md
@@ -0,0 +1,211 @@
+# Installing on NetBSD
+
+{! backend/installation/generic_dependencies.include !}
+
+## Installing software used in this guide
+
+pkgin should have been installed by the NetBSD installer if you selected
+the right options. If it isn't installed, install it using pkg_add.
+
+Note that `postgresql11-contrib` is needed for the Postgres extensions
+Pleroma uses.
+
+The `mksh` shell is needed to run the Elixir `mix` script.
+
+`# pkgin install acmesh elixir git-base git-docs mksh nginx postgresql11-server postgresql11-client postgresql11-contrib sudo ffmpeg4 ImageMagick`
+
+You can also build these packages using pkgsrc:
+```
+databases/postgresql11-contrib
+databases/postgresql11-client
+databases/postgresql11-server
+devel/git-base
+devel/git-docs
+devel/cmake
+lang/elixir
+security/acmesh
+security/sudo
+shells/mksh
+www/nginx
+```
+
+Copy the rc.d scripts to the right directory:
+
+```
+# cp /usr/pkg/share/examples/rc.d/nginx /usr/pkg/share/examples/rc.d/pgsql /etc/rc.d
+```
+
+Add nginx and Postgres to `/etc/rc.conf`:
+
+```
+nginx=YES
+pgsql=YES
+```
+
+## Configuring postgres
+
+First, run `# /etc/rc.d/pgsql start`. Then, `$ sudo -Hu pgsql -g pgsql createdb`.
+
+### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
+
+`# pkgin install ImageMagick ffmpeg4 p5-Image-ExifTool`
+
+## Configuring Pleroma
+
+Create a user for Pleroma:
+
+```
+# groupadd pleroma
+# useradd -d /home/pleroma -m -g pleroma -s /usr/pkg/bin/mksh pleroma
+# echo 'export LC_ALL="en_GB.UTF-8"' >> /home/pleroma/.profile
+# su -l pleroma -c $SHELL
+```
+
+Clone the repository:
+
+```
+$ cd /home/pleroma
+$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git
+```
+
+Configure Pleroma. Note that you need a domain name at this point:
+
+```
+$ cd /home/pleroma/pleroma
+$ mix deps.get
+$ MIX_ENV=prod mix pleroma.instance gen # You will be asked a few questions here.
+```
+
+Since Postgres is configured, we can now initialize the database. There should
+now be a file in `config/setup_db.psql` that makes this easier. Edit it, and
+*change the password* to a password of your choice. Make sure it is secure, since
+it'll be protecting your database. Now initialize the database:
+
+```
+$ sudo -Hu pgsql -g pgsql psql -f config/setup_db.psql
+```
+
+Postgres allows connections from all users without a password by default. To
+fix this, edit `/usr/pkg/pgsql/data/pg_hba.conf`. Change every `trust` to
+`password`.
+
+Once this is done, restart Postgres with `# /etc/rc.d/pgsql restart`.
+
+Run the database migrations.
+You will need to do this whenever you update with `git pull`:
+
+```
+$ MIX_ENV=prod mix ecto.migrate
+```
+
+## Configuring nginx
+
+Install the example configuration file
+`/home/pleroma/pleroma/installation/pleroma.nginx` to
+`/usr/pkg/etc/nginx.conf`.
+
+Note that it will need to be wrapped in a `http {}` block. You should add
+settings for the nginx daemon outside of the http block, for example:
+
+```
+user nginx nginx;
+error_log /var/log/nginx/error.log;
+worker_processes 4;
+
+events {
+}
+```
+
+Edit the defaults:
+
+* Change `ssl_certificate` and `ssl_trusted_certificate` to
+`/etc/nginx/tls/fullchain`.
+* Change `ssl_certificate_key` to `/etc/nginx/tls/key`.
+* Change `example.tld` to your instance's domain name.
+
+## Configuring acme.sh
+
+We'll be using acme.sh in Stateless Mode for TLS certificate renewal.
+
+First, get your account fingerprint:
+
+```
+$ sudo -Hu nginx -g nginx acme.sh --register-account
+```
+
+You need to add the following to your nginx configuration for the server
+running on port 80:
+
+```
+ location ~ ^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)$ {
+ default_type text/plain;
+ return 200 "$1.6fXAG9VyG0IahirPEU2ZerUtItW2DHzDzD9wZaEKpqd";
+ }
+```
+
+Replace the string after after `$1.` with your fingerprint.
+
+Start nginx:
+
+```
+# /etc/rc.d/nginx start
+```
+
+It should now be possible to issue a cert (replace `example.com`
+with your domain name):
+
+```
+$ sudo -Hu nginx -g nginx acme.sh --issue -d example.com --stateless
+```
+
+Let's add auto-renewal to `/etc/daily.local`
+(replace `example.com` with your domain):
+
+```
+/usr/pkg/bin/sudo -Hu nginx -g nginx \
+ /usr/pkg/sbin/acme.sh -r \
+ -d example.com \
+ --cert-file /etc/nginx/tls/cert \
+ --key-file /etc/nginx/tls/key \
+ --ca-file /etc/nginx/tls/ca \
+ --fullchain-file /etc/nginx/tls/fullchain \
+ --stateless
+```
+
+## Creating a startup script for Pleroma
+
+Copy the startup script to the correct location and make sure it's executable:
+
+```
+# cp /home/pleroma/pleroma/installation/netbsd/rc.d/pleroma /etc/rc.d/pleroma
+# chmod +x /etc/rc.d/pleroma
+```
+
+Add the following to `/etc/rc.conf`:
+
+```
+pleroma=YES
+pleroma_home="/home/pleroma"
+pleroma_user="pleroma"
+```
+
+Run `# /etc/rc.d/pleroma start` to start Pleroma.
+
+## Conclusion
+
+Restart nginx with `# /etc/rc.d/nginx restart` and you should be up and running.
+
+Make sure your time is in sync, or other instances will receive your posts with
+incorrect timestamps. You should have ntpd running.
+
+## Instances running NetBSD
+
+* <https://catgirl.science>
+
+#### Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/nixos_en.md b/docs/installation/nixos_en.md
new file mode 100644
index 0000000..f3c4988
--- /dev/null
+++ b/docs/installation/nixos_en.md
@@ -0,0 +1,15 @@
+# Installing on NixOS
+
+NixOS contains a source build package of pleroma and a NixOS module to install it.
+For installation add this to your configuration.nix and add a config.exs next to it:
+```nix
+ services.pleroma = {
+ enable = true;
+ configs = [ (lib.fileContents ./config.exs) ];
+ secretConfigFile = "/var/lib/pleroma/secret.exs";
+ };
+```
+
+## Questions
+The nix community uses matrix for communication: [#nix:nixos.org](https://matrix.to/#/#nix:nixos.org)
+
diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md
new file mode 100644
index 0000000..c80c8f6
--- /dev/null
+++ b/docs/installation/openbsd_en.md
@@ -0,0 +1,259 @@
+# Installing on OpenBSD
+
+This guide describes the installation and configuration of pleroma (and the required software to run it) on a single OpenBSD 6.6 server.
+
+For any additional information regarding commands and configuration files mentioned here, check the man pages [online](https://man.openbsd.org/) or directly on your server with the man command.
+
+{! backend/installation/generic_dependencies.include !}
+
+### Preparing the system
+#### Required software
+
+To install them, run the following command (with doas or as root):
+
+```
+pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick
+```
+
+Pleroma requires a reverse proxy, OpenBSD has relayd in base (and is used in this guide) and packages/ports are available for nginx (www/nginx) and apache (www/apache-httpd). Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acme-client) can be used to get a certificate from Let's Encrypt.
+
+#### Optional software
+
+Per [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md):
+ * ImageMagick
+ * ffmpeg
+ * exiftool
+
+To install the above:
+
+```
+pkg_add ImageMagick ffmpeg p5-Image-ExifTool
+```
+
+#### Creating the pleroma user
+Pleroma will be run by a dedicated user, \_pleroma. Before creating it, insert the following lines in login.conf:
+```
+pleroma:\
+ :datasize-max=1536M:\
+ :datasize-cur=1536M:\
+ :openfiles-max=4096
+```
+This creates a "pleroma" login class and sets higher values than default for datasize and openfiles (see [login.conf(5)](https://man.openbsd.org/login.conf)), this is required to avoid having pleroma crash some time after starting.
+
+Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma`
+
+#### Clone pleroma's directory
+Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone -b stable https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
+
+#### PostgreSQL
+Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:
+You will need to specify pgdata directory to the default (/var/postgresql/data) with the `-D <path>` and set the user to postgres with the `-U <username>` flag. This can be done as follows:
+
+```
+initdb -D /var/postgresql/data -U postgres
+```
+If you are not using the default directory, you will have to update the `datadir` variable in the /etc/rc.d/postgresql script.
+
+When this is done, enable postgresql so that it starts on boot and start it. As root, run:
+```
+rcctl enable postgresql
+rcctl start postgresql
+```
+To check that it started properly and didn't fail right after starting, you can run `ps aux | grep postgres`, there should be multiple lines of output.
+
+#### httpd
+httpd will have three fuctions:
+
+ * redirect requests trying to reach the instance over http to the https URL
+ * serve a robots.txt file
+ * get Let's Encrypt certificates, with acme-client
+
+Insert the following config in httpd.conf:
+```
+# $OpenBSD: httpd.conf,v 1.17 2017/04/16 08:50:49 ajacoutot Exp $
+
+ext_inet="<IPv4 address>"
+ext_inet6="<IPv6 address>"
+
+server "default" {
+ listen on $ext_inet port 80 # Comment to disable listening on IPv4
+ listen on $ext_inet6 port 80 # Comment to disable listening on IPv6
+ listen on 127.0.0.1 port 80 # Do NOT comment this line
+
+ log syslog
+ directory no index
+
+ location "/.well-known/acme-challenge/*" {
+ root "/acme"
+ request strip 2
+ }
+
+ location "/robots.txt" { root "/htdocs/local/" }
+ location "/*" { block return 302 "https://$HTTP_HOST$REQUEST_URI" }
+}
+
+types {
+}
+```
+Do not forget to change *<IPv4/6 address\>* to your server's address(es). If httpd should only listen on one protocol family, comment one of the two first *listen* options.
+
+Create the /var/www/htdocs/local/ folder and write the content of your robots.txt in /var/www/htdocs/local/robots.txt.
+Check the configuration with `httpd -n`, if it is OK enable and start httpd (as root):
+```
+rcctl enable httpd
+rcctl start httpd
+```
+
+#### acme-client
+acme-client is used to get SSL/TLS certificates from Let's Encrypt.
+Insert the following configuration in /etc/acme-client.conf:
+```
+#
+# $OpenBSD: acme-client.conf,v 1.4 2017/03/22 11:14:14 benno Exp $
+#
+
+authority letsencrypt-<domain name> {
+ #agreement url "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf"
+ api url "https://acme-v02.api.letsencrypt.org/directory"
+ account key "/etc/acme/letsencrypt-privkey-<domain name>.pem"
+}
+
+domain <domain name> {
+ domain key "/etc/ssl/private/<domain name>.key"
+ domain certificate "/etc/ssl/<domain name>.crt"
+ domain full chain certificate "/etc/ssl/<domain name>.fullchain.pem"
+ sign with letsencrypt-<domain name>
+ challengedir "/var/www/acme/"
+}
+```
+Replace *<domain name\>* by the domain name you'll use for your instance. As root, run `acme-client -n` to check the config, then `acme-client -ADv <domain name>` to create account and domain keys, and request a certificate for the first time.
+Make acme-client run everyday by adding it in /etc/daily.local. As root, run the following command: `echo "acme-client <domain name>" >> /etc/daily.local`.
+
+Relayd will look for certificates and keys based on the address it listens on (see next part), the easiest way to make them available to relayd is to create a link, as root run:
+```
+ln -s /etc/ssl/<domain name>.fullchain.pem /etc/ssl/<IP address>.crt
+ln -s /etc/ssl/private/<domain name>.key /etc/ssl/private/<IP address>.key
+```
+This will have to be done for each IPv4 and IPv6 address relayd listens on.
+
+#### relayd
+relayd will be used as the reverse proxy sitting in front of pleroma.
+Insert the following configuration in /etc/relayd.conf:
+```
+# $OpenBSD: relayd.conf,v 1.4 2018/03/23 09:55:06 claudio Exp $
+
+ext_inet="<IPv4 address>"
+ext_inet6="<IPv6 address>"
+
+table <pleroma_server> { 127.0.0.1 }
+table <httpd_server> { 127.0.0.1 }
+
+http protocol plerup { # Protocol for upstream pleroma server
+ #tcp { nodelay, sack, socket buffer 65536, backlog 128 } # Uncomment and adjust as you see fit
+ tls ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
+ tls ecdhe secp384r1
+
+ # Forward some paths to the local server (as pleroma won't respond to them as you might want)
+ pass request quick path "/robots.txt" forward to <httpd_server>
+
+ # Append a bunch of headers
+ match request header append "X-Forwarded-For" value "$REMOTE_ADDR" # This two header and the next one are not strictly required by pleroma but adding them won't hurt
+ match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT"
+
+ match response header append "X-XSS-Protection" value "1; mode=block"
+ match response header append "X-Permitted-Cross-Domain-Policies" value "none"
+ match response header append "X-Frame-Options" value "DENY"
+ match response header append "X-Content-Type-Options" value "nosniff"
+ match response header append "Referrer-Policy" value "same-origin"
+ match response header append "X-Download-Options" value "noopen"
+ match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'self'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://CHANGEME.tld; upgrade-insecure-requests;" # Modify "CHANGEME.tld" and set your instance's domain here
+ match request header append "Connection" value "upgrade"
+ #match response header append "Strict-Transport-Security" value "max-age=31536000; includeSubDomains" # Uncomment this only after you get HTTPS working.
+
+ # If you do not want remote frontends to be able to access your Pleroma backend server, comment these lines
+ match response header append "Access-Control-Allow-Origin" value "*"
+ match response header append "Access-Control-Allow-Methods" value "POST, PUT, DELETE, GET, PATCH, OPTIONS"
+ match response header append "Access-Control-Allow-Headers" value "Authorization, Content-Type, Idempotency-Key"
+ match response header append "Access-Control-Expose-Headers" value "Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id"
+ # Stop commenting lines here
+}
+
+relay wwwtls {
+ listen on $ext_inet port https tls # Comment to disable listening on IPv4
+ listen on $ext_inet6 port https tls # Comment to disable listening on IPv6
+
+ protocol plerup
+
+ forward to <pleroma_server> port 4000 check http "/" code 200
+ forward to <httpd_server> port 80 check http "/robots.txt" code 200
+}
+```
+Again, change *<IPv4/6 address\>* to your server's address(es) and comment one of the two *listen* options if needed. Also change *wss://CHANGEME.tld* to *wss://<your instance's domain name\>*.
+Check the configuration with `relayd -n`, if it is OK enable and start relayd (as root):
+```
+rcctl enable relayd
+rcctl start relayd
+```
+
+#### pf
+Enabling and configuring pf is highly recommended.
+In /etc/pf.conf, insert the following configuration:
+```
+# Macros
+if="<network interface>"
+authorized_ssh_clients="any"
+
+# Skip traffic on loopback interface
+set skip on lo
+
+# Default behavior
+set block-policy drop
+block in log all
+pass out quick
+
+# Security features
+match in all scrub (no-df random-id)
+block in log from urpf-failed
+
+# Rules
+pass in quick on $if inet proto icmp to ($if) icmp-type { echoreq unreach paramprob trace } # ICMP
+pass in quick on $if inet6 proto icmp6 to ($if) icmp6-type { echoreq unreach paramprob timex toobig } # ICMPv6
+pass in quick on $if proto tcp to ($if) port { http https } # relayd/httpd
+pass in quick on $if proto tcp from $authorized_ssh_clients to ($if) port ssh
+```
+Replace *<network interface\>* by your server's network interface name (which you can get with ifconfig). Consider replacing the content of the authorized\_ssh\_clients macro by, for exemple, your home IP address, to avoid SSH connection attempts from bots.
+
+Check pf's configuration by running `pfctl -nf /etc/pf.conf`, load it with `pfctl -f /etc/pf.conf` and enable pf at boot with `rcctl enable pf`.
+
+#### Configure and start pleroma
+Enter a shell as \_pleroma (as root `su _pleroma -`) and enter pleroma's installation directory (`cd ~/pleroma/`).
+
+Then follow the main installation guide:
+
+ * run `mix deps.get`
+ * run `MIX_ENV=prod mix pleroma.instance gen` and enter your instance's information when asked
+ * copy config/generated\_config.exs to config/prod.secret.exs. The default values should be sufficient but you should edit it and check that everything seems OK.
+ * exit your current shell back to a root one and run `psql -U postgres -f /home/_pleroma/pleroma/config/setup_db.psql` to setup the database.
+ * return to a \_pleroma shell into pleroma's installation directory (`su _pleroma -;cd ~/pleroma`) and run `MIX_ENV=prod mix ecto.migrate`
+
+As \_pleroma in /home/\_pleroma/pleroma, you can now run `LC_ALL=en_US.UTF-8 MIX_ENV=prod mix phx.server` to start your instance.
+In another SSH session/tmux window, check that it is working properly by running `ftp -MVo - http://127.0.0.1:4000/api/v1/instance`, you should get json output. Double-check that *uri*'s value is your instance's domain name.
+
+##### Starting pleroma at boot
+An rc script to automatically start pleroma at boot hasn't been written yet, it can be run in a tmux session (tmux is in base).
+
+
+#### Create administrative user
+
+If your instance is up and running, you can create your first user with administrative rights with the following command as the \_pleroma user.
+```
+LC_ALL=en_US.UTF-8 MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
+```
+
+#### Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md
new file mode 100644
index 0000000..3c40b2d
--- /dev/null
+++ b/docs/installation/openbsd_fi.md
@@ -0,0 +1,121 @@
+# Pleroman asennus OpenBSD:llä
+
+Tarvitset:
+* Oman domainin
+* OpenBSD 6.3 -serverin
+* Auttavan ymmärryksen unix-järjestelmistä
+
+Komennot, joiden edessä on '#', tulee ajaa käyttäjänä `root`. Tämä on
+suositeltavaa tehdä komennon `doas` avulla, katso `doas (1)` ja `doas.conf (5)`.
+Tästä eteenpäin oletuksena on, että domain "esimerkki.com" osoittaa
+serverin IP-osoitteeseen.
+
+Jos asennuksen kanssa on ongelmia, IRC-kanava #pleroma Libera.chat tai
+Matrix-kanava #pleroma:libera.chat ovat hyviä paikkoja löytää apua
+(englanniksi), `/msg eal kukkuu` jos haluat välttämättä puhua härmää.
+
+Asenna tarvittava ohjelmisto:
+
+`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake ffmpeg ImageMagick`
+
+#### Optional software
+
+[`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md):
+ * ImageMagick
+ * ffmpeg
+ * exiftool
+
+Asenna tarvittava ohjelmisto:
+
+`# pkg_add ImageMagick ffmpeg p5-Image-ExifTool`
+
+Luo postgresql-tietokanta:
+
+`# su - _postgresql`
+
+`$ mkdir /var/postgresql/data`
+
+`$ initdb -D /var/postgresql/data -E UTF8`
+
+`$ createdb`
+
+Käynnistä tietokanta ja aseta se käynnistymään automaattisesti.
+
+`# rcctl start postgresql`
+
+`# rcctl enable postgresql`
+
+Luo käyttäjä pleromaa varten (kysyy muutaman kysymyksen):
+
+`# adduser pleroma`
+
+Vaihda pleroma-käyttäjään ja mene kotihakemistoosi:
+
+`# su - pleroma`
+
+Lataa pleroman lähdekoodi:
+
+`$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git`
+
+`$ cd pleroma`
+
+Asenna tarvittavat elixir-kirjastot:
+
+`$ mix deps.get`
+
+`$ mix deps.compile`
+
+Luo tarvittava konfiguraatio:
+
+`$ mix generate_config`
+
+`$ cp config/generated_config.exs config/prod.secret.exs`
+
+Aja luodut tietokantakomennot:
+
+`# su _postgres -c 'psql -f config/setup_db.psql'`
+
+`$ MIX_ENV=prod mix ecto.migrate`
+
+Käynnistä pleroma-prosessi:
+
+`$ MIX_ENV=prod mix compile`
+
+`$ MIX_ENV=prod mix phx.server`
+
+Tässä vaiheessa on hyvä tarkistaa että asetukset ovat oikein. Avaa selaimella,
+curlilla tai vastaavalla työkalulla `esimerkki.com:4000/api/v1/instance` ja katso
+että kohta "uri" on "https://esimerkki.com".
+
+Huom! Muista varmistaa että muuttuja MIX_ENV on "prod" mix-komentoja ajaessasi.
+Mix lukee oikean konfiguraatiotiedoston sen mukaisesti.
+
+Ohessa enimmäkseen toimivaksi todettu rc.d-skripti pleroman käynnistämiseen.
+Kirjoita se tiedostoon /etc/rc.d/pleroma. Tämän jälkeen aja
+`# chmod +x /etc/rc.d/pleroma`, ja voit käynnistää pleroman komennolla
+`# /etc/rc.d/pleroma start`.
+
+```
+#!/bin/ksh
+#/etc/rc.d/pleroma
+
+daemon="cd /home/pleroma/pleroma;MIX_ENV=prod /usr/local/bin/elixir"
+daemon_flags="--detached /usr/local/bin/mix phx.server"
+daemon_user="pleroma"
+rc_reload="NO"
+rc_bg="YES"
+
+pexp="beam"
+
+. /etc/rc.d/rc.subr
+
+rc_cmd $1
+```
+
+Tämän jälkeen tarvitset enää HTTP-serverin välittämään kutsut pleroma-prosessille.
+Tiedostosta `install/pleroma.nginx` löytyy esimerkkikonfiguraatio, ja TLS-sertifikaatit
+saat ilmaiseksi esimerkiksi [letsencryptiltä](https://certbot.eff.org/lets-encrypt/opbsd-nginx.html).
+Nginx asentuu yksinkertaisesti komennolla `# pkg_add nginx`.
+
+Kun olet valmis, avaa https://esimerkki.com selaimessasi. Luo käyttäjä ja seuraa kiinnostavia
+tyyppejä muilla palvelimilla!
diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md
new file mode 100644
index 0000000..de402d1
--- /dev/null
+++ b/docs/installation/optional/media_graphics_packages.md
@@ -0,0 +1,33 @@
+# Optional software packages needed for specific functionality
+
+For specific Pleroma functionality (which is disabled by default) some or all of the below packages are required:
+ * `ImageMagic`
+ * `ffmpeg`
+ * `exiftool`
+
+Please refer to documentation in `docs/installation` on how to install them on specific OS.
+
+Note: the packages are not required with the current default settings of Pleroma.
+
+## `ImageMagick`
+
+`ImageMagick` is a set of tools to create, edit, compose, or convert bitmap images.
+
+It is required for the following Pleroma features:
+ * `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`)
+ * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`)
+
+## `ffmpeg`
+
+`ffmpeg` is software to record, convert and stream audio and video.
+
+It is required for the following Pleroma features:
+ * Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`)
+
+## `exiftool`
+
+`exiftool` is media files metadata reader/writer.
+
+It is required for the following Pleroma features:
+ * `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`)
+ * `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`)
diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md
new file mode 100644
index 0000000..f281234
--- /dev/null
+++ b/docs/installation/otp_en.md
@@ -0,0 +1,308 @@
+# Installing on Linux using OTP releases
+
+{! backend/installation/otp_vs_from_source.include !}
+
+This guide covers a installation using OTP releases as built by the Pleroma project, it is meant as a fallback to distribution packages/recipes which are the preferred installation method.
+To install Pleroma from source, please check out the corresponding guide for your distro.
+
+## Pre-requisites
+* A machine you have root access to running Debian GNU/Linux or compatible (eg. Ubuntu), or Alpine on `x86_64`, `aarch64` or `armv7l` CPU. If you are not sure what you are running see [Detecting flavour section](#detecting-flavour) below
+* A (sub)domain pointed to the machine
+
+You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo -i`/`su`.
+
+Similarly to other binaries, OTP releases tend to be only compatible with the distro they are built on, as such this guide focuses only on Debian/Ubuntu and Alpine.
+
+### Detecting flavour
+
+Paste the following into the shell:
+```sh
+arch="$(uname -m)";if [ "$arch" = "x86_64" ];then arch="amd64";elif [ "$arch" = "armv7l" ];then arch="arm";elif [ "$arch" = "aarch64" ];then arch="arm64";else echo "Unsupported arch: $arch">&2;fi;if getconf GNU_LIBC_VERSION>/dev/null;then libc_postfix="";elif [ "$(ldd 2>&1|head -c 9)" = "musl libc" ];then libc_postfix="-musl";elif [ "$(find /lib/libc.musl*|wc -l)" ];then libc_postfix="-musl";else echo "Unsupported libc">&2;fi;echo "$arch$libc_postfix"
+```
+
+This should give your flavour string. If not this just means that we don't build releases for your platform, you can still try installing from source.
+
+### Installing the required packages
+
+Other than things bundled in the OTP release Pleroma depends on:
+
+* curl (to download the release build)
+* unzip (needed to unpack release builds)
+* ncurses (ERTS won't run without it)
+* PostgreSQL (also utilizes extensions in postgresql-contrib)
+* nginx (could be swapped with another reverse proxy but this guide covers only it)
+* certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it)
+* libmagic/file
+
+=== "Alpine"
+ ```
+ awk 'NR==2' /etc/apk/repositories | sed 's/main/community/' | tee -a /etc/apk/repositories
+ apk update
+ apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot file-dev
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot libmagic-dev
+ ```
+
+### Installing optional packages
+
+Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_graphics_packages.md):
+ * ImageMagick
+ * ffmpeg
+ * exiftool
+
+=== "Alpine"
+ ```
+ apk update
+ apk add imagemagick ffmpeg exiftool
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ apt install imagemagick ffmpeg libimage-exiftool-perl
+ ```
+
+## Setup
+### Configuring PostgreSQL
+#### (Optional) Installing RUM indexes
+
+!!! warning
+ It is recommended to use PostgreSQL v11 or newer. We have seen some minor issues with lower PostgreSQL versions.
+
+RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. You can read more about them on the [Configuration page](../configuration/cheatsheet.md#rum-indexing-for-full-text-search). They are completely optional and most of the time are not worth it, especially if you are running a single user instance (unless you absolutely need ordered search results).
+
+=== "Alpine"
+ ```
+ apk add git build-base postgresql-dev
+ git clone https://github.com/postgrespro/rum /tmp/rum
+ cd /tmp/rum
+ make USE_PGXS=1
+ make USE_PGXS=1 install
+ cd
+ rm -r /tmp/rum
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ # Available only on Buster/19.04
+ apt install postgresql-11-rum
+ ```
+
+#### (Optional) Performance configuration
+It is encouraged to check [Optimizing your PostgreSQL performance](../configuration/postgresql.md) document, for tips on PostgreSQL tuning.
+
+Restart PostgreSQL to apply configuration changes:
+
+=== "Alpine"
+ ```
+ rc-service postgresql restart
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ systemctl restart postgresql
+ ```
+
+### Installing Pleroma
+```sh
+# Create a Pleroma user
+adduser --system --shell /bin/false --home /opt/pleroma pleroma
+
+# Set the flavour environment variable to the string you got in Detecting flavour section.
+# For example if the flavour is `amd64-musl` the command will be
+export FLAVOUR="amd64-musl"
+
+# Clone the release build into a temporary directory and unpack it
+su pleroma -s $SHELL -lc "
+curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
+unzip /tmp/pleroma.zip -d /tmp/
+"
+
+# Move the release to the home directory and delete temporary files
+su pleroma -s $SHELL -lc "
+mv /tmp/release/* /opt/pleroma
+rmdir /tmp/release
+rm /tmp/pleroma.zip
+"
+# Create uploads directory and set proper permissions (skip if planning to use a remote uploader)
+# Note: It does not have to be `/var/lib/pleroma/uploads`, the config generator will ask about the upload directory later
+
+mkdir -p /var/lib/pleroma/uploads
+chown -R pleroma /var/lib/pleroma
+
+# Create custom public files directory (custom emojis, frontend bundle overrides, robots.txt, etc.)
+# Note: It does not have to be `/var/lib/pleroma/static`, the config generator will ask about the custom public files directory later
+mkdir -p /var/lib/pleroma/static
+chown -R pleroma /var/lib/pleroma
+
+# Create a config directory
+mkdir -p /etc/pleroma
+chown -R pleroma /etc/pleroma
+
+# Run the config generator
+su pleroma -s $SHELL -lc "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql"
+
+# Create the postgres database
+su postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql"
+
+# Create the database schema
+su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
+
+# If you have installed RUM indexes uncommend and run
+# su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
+
+# Start the instance to verify that everything is working as expected
+su pleroma -s $SHELL -lc "./bin/pleroma daemon"
+
+# Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly
+sleep 20 && curl http://localhost:4000/api/v1/instance
+
+# Stop the instance
+su pleroma -s $SHELL -lc "./bin/pleroma stop"
+```
+
+### Setting up nginx and getting Let's Encrypt SSL certificaties
+
+#### Get a Let's Encrypt certificate
+```sh
+certbot certonly --standalone --preferred-challenges http -d yourinstance.tld
+```
+
+#### Copy Pleroma nginx configuration to the nginx folder
+
+The location of nginx configs is dependent on the distro
+
+=== "Alpine"
+ ```
+ cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.conf
+ ln -s /etc/nginx/sites-available/pleroma.conf /etc/nginx/sites-enabled/pleroma.conf
+ ```
+
+If your distro does not have either of those you can append `include /etc/nginx/pleroma.conf` to the end of the http section in /etc/nginx/nginx.conf and
+```sh
+cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/pleroma.conf
+```
+
+#### Edit the nginx config
+```sh
+# Replace example.tld with your (sub)domain
+$EDITOR path-to-nginx-config
+
+# Verify that the config is valid
+nginx -t
+```
+#### Start nginx
+
+=== "Alpine"
+ ```
+ rc-service nginx start
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ systemctl start nginx
+ ```
+
+At this point if you open your (sub)domain in a browser you should see a 502 error, that's because Pleroma is not started yet.
+
+### Setting up a system service
+
+=== "Alpine"
+ ```
+ # Copy the service into a proper directory
+ cp /opt/pleroma/installation/init.d/pleroma /etc/init.d/pleroma
+
+ # Start pleroma and enable it on boot
+ rc-service pleroma start
+ rc-update add pleroma
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ # Copy the service into a proper directory
+ cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
+
+ # Start pleroma and enable it on boot
+ systemctl start pleroma
+ systemctl enable pleroma
+ ```
+
+If everything worked, you should see Pleroma-FE when visiting your domain. If that didn't happen, try reviewing the installation steps, starting Pleroma in the foreground and seeing if there are any errrors.
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new).
+
+## Post installation
+
+### Setting up auto-renew of the Let's Encrypt certificate
+```sh
+# Create the directory for webroot challenges
+mkdir -p /var/lib/letsencrypt
+
+# Uncomment the webroot method
+$EDITOR path-to-nginx-config
+
+# Verify that the config is valid
+nginx -t
+```
+
+=== "Alpine"
+ ```
+ # Restart nginx
+ rc-service nginx restart
+
+ # Start the cron daemon and make it start on boot
+ rc-service crond start
+ rc-update add crond
+
+ # Ensure the webroot menthod and post hook is working
+ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'rc-service nginx reload'
+
+ # Add it to the daily cron
+ echo '#!/bin/sh
+ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "rc-service nginx reload"
+ ' > /etc/periodic/daily/renew-pleroma-cert
+ chmod +x /etc/periodic/daily/renew-pleroma-cert
+
+ # If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert
+ run-parts --test /etc/periodic/daily
+ ```
+
+=== "Debian/Ubuntu"
+ ```
+ # Restart nginx
+ systemctl restart nginx
+
+ # Ensure the webroot menthod and post hook is working
+ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'systemctl reload nginx'
+
+ # Add it to the daily cron
+ echo '#!/bin/sh
+ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx"
+ ' > /etc/cron.daily/renew-pleroma-cert
+ chmod +x /etc/cron.daily/renew-pleroma-cert
+
+ # If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert
+ run-parts --test /etc/cron.daily
+ ```
+
+## Create your first user and set as admin
+```sh
+cd /opt/pleroma
+su pleroma -s $SHELL -lc "./bin/pleroma_ctl user new joeuser joeuser@sld.tld --admin"
+```
+This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password.
+
+## Further reading
+
+{! backend/installation/further_reading.include !}
+
+## Questions
+
+Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new).
diff --git a/docs/installation/otp_vs_from_source.include b/docs/installation/otp_vs_from_source.include
new file mode 100644
index 0000000..63e837a
--- /dev/null
+++ b/docs/installation/otp_vs_from_source.include
@@ -0,0 +1,3 @@
+## OTP releases vs from-source installations
+
+There are two ways to install Pleroma. You can use OTP releases or do a from-source installation. OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more. With from source installations you install Pleroma from source, meaning you have to install certain dependencies like Erlang+Elixir and compile Pleroma yourself.
diff --git a/docs/installation/otp_vs_from_source_source.include b/docs/installation/otp_vs_from_source_source.include
new file mode 100644
index 0000000..63482b6
--- /dev/null
+++ b/docs/installation/otp_vs_from_source_source.include
@@ -0,0 +1,3 @@
+{! backend/installation/otp_vs_from_source.include !}
+
+This guide covers a from-source installation. To install using OTP releases, please check out [the OTP guide](./otp_en.md).
diff --git a/docs/installation/yunohost_en.md b/docs/installation/yunohost_en.md
new file mode 100644
index 0000000..4c34e85
--- /dev/null
+++ b/docs/installation/yunohost_en.md
@@ -0,0 +1,9 @@
+# Installing on Yunohost
+
+[YunoHost](https://yunohost.org) is a server operating system aimed at self-hosting. The YunoHost community maintains a package of Pleroma which allows you to install Pleroma on YunoHost. You can install it via the normal way through the admin web interface, or through the CLI. More information can be found at [the repo of the package](https://github.com/YunoHost-Apps/pleroma_ynh).
+
+## Questions
+
+Questions and problems related to the YunoHost parts can be done through the [regular YunoHost channels](https://yunohost.org/en/help).
+
+For questions about Pleroma, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
diff --git a/docs/installation_1/apache-cache-purge.sh.example b/docs/installation_1/apache-cache-purge.sh.example
new file mode 100755
index 0000000..7b42628
--- /dev/null
+++ b/docs/installation_1/apache-cache-purge.sh.example
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# A simple shell script to delete a media from Apache's mod_disk_cache.
+# You will likely need to setup a sudo rule like the following:
+#
+# Cmnd_Alias HTCACHECLEAN = /usr/local/sbin/htcacheclean
+# pleroma ALL=HTCACHECLEAN, NOPASSWD: HTCACHECLEAN
+#
+# Please also ensure you have enabled:
+#
+# config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script, url_format: :htcacheclean
+#
+# which will correctly format the URLs passed to this script for the htcacheclean utility.
+#
+
+SCRIPTNAME=${0##*/}
+
+# mod_disk_cache directory
+CACHE_DIRECTORY="/tmp/pleroma-media-cache"
+
+## Removes an item via the htcacheclean utility
+## $1 - the filename, can be a pattern .
+## $2 - the cache directory.
+purge_item() {
+ sudo htcacheclean -v -p "${2}" "${1}"
+} # purge_item
+
+purge() {
+ for url in $@
+ do
+ echo "$SCRIPTNAME delete \`$url\` from cache ($CACHE_DIRECTORY)"
+ purge_item "$url" $CACHE_DIRECTORY
+ done
+}
+
+purge $@
diff --git a/docs/installation_1/caddyfile-pleroma.example b/docs/installation_1/caddyfile-pleroma.example
new file mode 100644
index 0000000..cc7dda0
--- /dev/null
+++ b/docs/installation_1/caddyfile-pleroma.example
@@ -0,0 +1,17 @@
+# default Caddyfile config for Pleroma
+#
+# Simple installation instructions:
+# 1. Replace 'example.tld' with your instance's domain wherever it appears.
+# 2. Copy this section into your Caddyfile and restart Caddy.
+
+example.tld {
+ log {
+ output file /var/log/caddy/pleroma.log
+ }
+
+ encode gzip
+
+ # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
+ # and `localhost.` resolves to [::0] on some systems: see issue #930
+ reverse_proxy 127.0.0.1:4000
+}
diff --git a/docs/installation_1/freebsd/rc.d/pleroma b/docs/installation_1/freebsd/rc.d/pleroma
new file mode 100755
index 0000000..f62aef1
--- /dev/null
+++ b/docs/installation_1/freebsd/rc.d/pleroma
@@ -0,0 +1,27 @@
+#!/bin/sh
+# $FreeBSD$
+# PROVIDE: pleroma
+# REQUIRE: DAEMON postgresql
+# KEYWORD: shutdown
+
+# sudo -u pleroma MIX_ENV=prod elixir --erl \"-detached\" -S mix phx.server
+
+. /etc/rc.subr
+
+name=pleroma
+rcvar=pleroma_enable
+
+desc="Pleroma Social Media Platform"
+
+load_rc_config ${name}
+
+: ${pleroma_user:=pleroma}
+: ${pleroma_home:=$(getent passwd ${pleroma_user} | awk -F: '{print $6}')}
+: ${pleroma_chdir:="${pleroma_home}/pleroma"}
+: ${pleroma_env:="HOME=${pleroma_home} MIX_ENV=prod"}
+
+command=/usr/local/bin/elixir
+command_args="--erl \"-detached\" -S /usr/local/bin/mix phx.server"
+procname="*beam.smp"
+
+run_rc_command "$1"
diff --git a/docs/installation_1/init.d/pleroma b/docs/installation_1/init.d/pleroma
new file mode 100755
index 0000000..384536f
--- /dev/null
+++ b/docs/installation_1/init.d/pleroma
@@ -0,0 +1,45 @@
+#!/sbin/openrc-run
+supervisor=supervise-daemon
+command_user=pleroma:pleroma
+command_background=1
+# Ask process to terminate within 30 seconds, otherwise kill it
+retry="SIGTERM/30/SIGKILL/5"
+pidfile="/var/run/pleroma.pid"
+directory=/opt/pleroma
+healthcheck_delay=60
+healthcheck_timer=30
+
+: ${pleroma_port:-4000}
+
+# Needs OpenRC >= 0.42
+#respawn_max=0
+#respawn_delay=5
+
+# put pleroma_console=YES in /etc/conf.d/pleroma if you want to be able to
+# connect to pleroma via an elixir console
+if yesno "${pleroma_console}"; then
+ command=elixir
+ command_args="--name pleroma@127.0.0.1 --erl '-kernel inet_dist_listen_min 9001 inet_dist_listen_max 9001 inet_dist_use_interface {127,0,0,1}' -S mix phx.server"
+
+ start_post() {
+ einfo "You can get a console by using this command as pleroma's user:"
+ einfo "iex --name console@127.0.0.1 --remsh pleroma@127.0.0.1"
+ }
+else
+ command=/usr/bin/mix
+ command_args="phx.server"
+fi
+
+export MIX_ENV=prod
+
+depend() {
+ need nginx postgresql
+}
+
+healthcheck() {
+ # put pleroma_health=YES in /etc/conf.d/pleroma if you want healthchecking
+ # and make sure you have curl installed
+ yesno "$pleroma_health" || return 0
+
+ curl -q "localhost:${pleroma_port}/api/pleroma/healthcheck"
+}
diff --git a/docs/installation_1/netbsd/rc.d/pleroma b/docs/installation_1/netbsd/rc.d/pleroma
new file mode 100755
index 0000000..1114668
--- /dev/null
+++ b/docs/installation_1/netbsd/rc.d/pleroma
@@ -0,0 +1,57 @@
+#!/bin/sh
+# PROVIDE: pleroma
+# REQUIRE: DAEMON pgsql
+
+if [ -f /etc/rc.subr ]; then
+ . /etc/rc.subr
+fi
+
+name="pleroma"
+rcvar=${name}
+command="/usr/pkg/bin/elixir"
+command_args="--detached -S /usr/pkg/bin/mix phx.server"
+start_precmd="ulimit -n unlimited"
+pidfile="/dev/null"
+
+pleroma_chdir="${pleroma_home}/pleroma"
+pleroma_env="HOME=${pleroma_home} MIX_ENV=prod"
+
+check_pidfile()
+{
+ pid=$(pgrep -U "${pleroma_user}" /bin/beam.smp$)
+ echo -n "${pid}"
+}
+
+if [ -f /etc/rc.subr -a -d /etc/rc.d -a -f /etc/rc.d/DAEMON ]; then
+ # newer NetBSD
+ load_rc_config ${name}
+ run_rc_command "$1"
+else
+ # ancient NetBSD, Solaris and illumos, Linux, etc...
+ cmd=${1:-start}
+
+ case ${cmd} in
+ start)
+ echo "Starting ${name}."
+ ${start_cmd}
+ ;;
+
+ stop)
+ echo "Stopping ${name}."
+ check_pidfile
+ ! [ -n ${pid} ] && kill ${pid}
+ ;;
+
+ restart)
+ ( $0 stop )
+ sleep 5
+ $0 start
+ ;;
+
+ *)
+ echo 1>&2 "Usage: $0 [start|stop|restart]"
+ exit 1
+ ;;
+ esac
+ exit 0
+fi
diff --git a/docs/installation_1/nginx-cache-purge.sh.example b/docs/installation_1/nginx-cache-purge.sh.example
new file mode 100755
index 0000000..5f6cbb1
--- /dev/null
+++ b/docs/installation_1/nginx-cache-purge.sh.example
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# A simple shell script to delete a media from the Nginx cache.
+
+SCRIPTNAME=${0##*/}
+
+# NGINX cache directory
+CACHE_DIRECTORY="/tmp/pleroma-media-cache"
+
+## Return the files where the items are cached.
+## $1 - the filename, can be a pattern .
+## $2 - the cache directory.
+## $3 - (optional) the number of parallel processes to run for grep.
+get_cache_files() {
+ local max_parallel=${3-16}
+ find $2 -maxdepth 2 -type d | xargs -P $max_parallel -n 1 grep -E -Rl "^KEY:.*$1" | sort -u
+}
+
+## Removes an item from the given cache zone.
+## $1 - the filename, can be a pattern .
+## $2 - the cache directory.
+purge_item() {
+ for f in $(get_cache_files $1 $2); do
+ echo "found file: $f"
+ [ -f $f ] || continue
+ echo "Deleting $f from $2."
+ rm $f
+ done
+} # purge_item
+
+purge() {
+ for url in "$@"
+ do
+ echo "$SCRIPTNAME delete \`$url\` from cache ($CACHE_DIRECTORY)"
+ purge_item $url $CACHE_DIRECTORY
+ done
+
+}
+
+purge $@
diff --git a/docs/installation_1/openbsd/httpd.conf b/docs/installation_1/openbsd/httpd.conf
new file mode 100644
index 0000000..82f4803
--- /dev/null
+++ b/docs/installation_1/openbsd/httpd.conf
@@ -0,0 +1,36 @@
+#
+# Default httpd.conf file for Pleroma on OpenBSD
+# Simple installation instructions
+# 1. Place file in /etc
+# 2. Replace <IPv4 address> with your public IP address
+# 3. If using IPv6, uncomment IPv6 lines and replace <IPv6 address> with your public IPv6 address
+# 4. Check file using 'doas httpd -n'
+# 5. Enable and start httpd:
+# # doas rcctl enable httpd
+# # doas rcctl start httpd
+#
+
+ext_inet="<IPv4 address>"
+#ext_inet6="<IPv6 address>"
+
+server "default" {
+ listen on $ext_inet port 80 # Comment to disable listening on IPv4
+# listen on $ext_inet6 port 80 # Comment to disable listening on IPv6
+ listen on 127.0.0.1 port 80 # Do NOT comment this line
+
+ log syslog
+ directory no index
+
+ location "/.well-known/acme-challenge/*" {
+ root "/acme"
+ request strip 2
+ }
+
+ location "/robots.txt" { root "/htdocs/local/" }
+ location "/*" { block return 302 "https://$HTTP_HOST$REQUEST_URI" }
+}
+
+types {
+ include "/usr/share/misc/mime.types"
+}
+
diff --git a/docs/installation_1/openbsd/rc.d/pleromad b/docs/installation_1/openbsd/rc.d/pleromad
new file mode 100755
index 0000000..19ac4bb
--- /dev/null
+++ b/docs/installation_1/openbsd/rc.d/pleromad
@@ -0,0 +1,34 @@
+#!/bin/ksh
+#
+# Default init file for Pleroma on OpenBSD
+#
+# Simple installation instructions:
+# 1. Install Pleroma per wiki instructions
+# 2. Place this pleromad file in /etc/rc.d
+# 3. Enable and start Pleroma
+# # doas rcctl enable pleromad
+# # doas rcctl start pleromad
+#
+
+daemon="/usr/local/bin/elixir"
+daemon_flags="--detached -S /usr/local/bin/mix phx.server"
+daemon_user="_pleroma"
+
+. /etc/rc.d/rc.subr
+
+rc_reload=NO
+pexp="phx.server"
+
+rc_check() {
+ pgrep -q -U _pleroma -f "phx.server"
+}
+
+rc_start() {
+ ${rcexec} "cd pleroma; ${daemon} ${daemon_flags}"
+}
+
+rc_stop() {
+ pkill -q -U _pleroma -f "phx.server"
+}
+
+rc_cmd $1
diff --git a/docs/installation_1/openbsd/relayd.conf b/docs/installation_1/openbsd/relayd.conf
new file mode 100644
index 0000000..31c2c11
--- /dev/null
+++ b/docs/installation_1/openbsd/relayd.conf
@@ -0,0 +1,44 @@
+#
+# Default relayd.conf file for Pleroma on OpenBSD
+# Simple installation instructions:
+# 1. Place in /etc
+# 2. Replace <ipaddr> with your public IPv4 address
+# 3. If using IPv6i, uncomment IPv6 lines and replace <ip6addr> with your public IPv6 address
+# 4. Check file using 'doas relayd -n'
+# 5. Reload/start relayd
+# # doas rcctl enable relayd
+# # doas rcctl start relayd
+#
+
+ext_inet="<ipaddr>"
+#ext_inet6="<ip6addr>"
+
+table <pleroma_server> { 127.0.0.1 }
+table <httpd_server> { 127.0.0.1 }
+
+http protocol plerup { # Protocol for upstream pleroma server
+ #tcp { nodelay, sack, socket buffer 65536, backlog 128 } # Uncomment and adjust as you see fit
+ tls ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA0-POLY1305"
+ tls ecdhe secp384r1
+
+ # Forward some paths to the local server (as pleroma won't respond to them as you might want)
+ pass request quick path "/robots.txt" forward to <httpd_server>
+
+ # Append a bunch of headers
+ match request header append "X-Forwarded-For" value "$REMOTE_ADDR" # This two header and the next one are not strictl required by pleroma but adding them won't hurt
+ match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT"
+
+ match request header append "Connection" value "upgrade"
+
+}
+
+relay wwwtls {
+ listen on $ext_inet port https tls # Comment to disable listening on IPv4
+# listen on $ext_inet6 port https tls # Comment to disable listening on IPv6
+
+ protocol plerup
+
+ forward to <pleroma_server> port 4000 check http "/" code 200
+ forward to <httpd_server> port 80 check http "/robots.txt" code 200
+}
+
diff --git a/docs/installation_1/pleroma-apache.conf b/docs/installation_1/pleroma-apache.conf
new file mode 100644
index 0000000..139abe9
--- /dev/null
+++ b/docs/installation_1/pleroma-apache.conf
@@ -0,0 +1,84 @@
+# Sample Apache config for Pleroma
+#
+# Simple installation instructions:
+# 1. Install your TLS certificate. We recommend using Let's Encrypt via Certbot
+# 2. Replace 'example.tld' with your instance's domain.
+# 3. This assumes a Debian-style Apache config. Copy this file to
+# /etc/apache2/sites-available/ and then activate the site by running
+# 'a2ensite pleroma-apache.conf', then restart Apache.
+#
+# Optional: enable disk-based caching for the media proxy
+# For details, see https://git.pleroma.social/pleroma/pleroma/wikis/How%20to%20activate%20mediaproxy
+#
+# 1. Create a directory as shown below for the CacheRoot and make sure
+# the Apache user can write to it.
+# 2. Configure Apache's htcacheclean to clean the directory periodically.
+# Your OS may provide a service you can enable to do this automatically.
+
+Define servername example.tld
+
+<IfModule !proxy_module>
+ LoadModule proxy_module libexec/apache24/mod_proxy.so
+</IfModule>
+<IfModule !proxy_http_module>
+ LoadModule proxy_http_module libexec/apache24/mod_proxy_http.so
+</IfModule>
+<IfModule !proxy_wstunnel_module>
+ LoadModule proxy_wstunnel_module libexec/apache24/mod_proxy_wstunnel.so
+</IfModule>
+<IfModule !rewrite_module>
+ LoadModule rewrite_module libexec/apache24/mod_rewrite.so
+</IfModule>
+<IfModule !ssl_module>
+ LoadModule ssl_module libexec/apache24/mod_ssl.so
+</IfModule>
+<IfModule !cache_module>
+ LoadModule cache_module libexec/apache24/mod_cache.so
+</IfModule>
+<IfModule !cache_disk_module>
+ LoadModule cache_disk_module libexec/apache24/mod_cache_disk.so
+</IfModule>
+
+ServerName ${servername}
+ServerTokens Prod
+
+# If you want Pleroma-specific logs
+#ErrorLog /var/log/httpd-pleroma-error.log
+#CustomLog /var/log/httpd-pleroma-access.log combined
+
+<VirtualHost *:80>
+ RewriteEngine on
+ RewriteCond %{SERVER_NAME} =${servername}
+ RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
+</VirtualHost>
+
+<VirtualHost *:443>
+ SSLEngine on
+ SSLCertificateFile /etc/letsencrypt/live/${servername}/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/${servername}/privkey.pem
+ # Make sure you have the certbot-apache module installed
+ Include /etc/letsencrypt/options-ssl-apache.conf
+
+ # Uncomment the following to enable MediaProxy caching on disk
+ #CacheRoot /tmp/pleroma-media-cache/
+ #CacheDirLevels 1
+ #CacheDirLength 2
+ #CacheEnable disk /proxy
+ #CacheLock on
+ #CacheHeader on
+ #CacheDetailHeader on
+ ## 16MB max filesize for caching, configure as desired
+ #CacheMaxFileSize 16000000
+ #CacheDefaultExpire 86400
+
+ RewriteEngine On
+ RewriteCond %{HTTP:Connection} Upgrade [NC]
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
+ RewriteRule /(.*) ws://127.0.0.1:4000/$1 [P,L]
+
+ #ProxyRequests must be off or you open your server to abuse as an open proxy
+ ProxyRequests off
+ ProxyPass / http://127.0.0.1:4000/
+ ProxyPassReverse / http://127.0.0.1:4000/
+ ProxyPreserveHost On
+</VirtualHost>
diff --git a/docs/installation_1/pleroma-mongooseim.cfg b/docs/installation_1/pleroma-mongooseim.cfg
new file mode 100755
index 0000000..3ecba56
--- /dev/null
+++ b/docs/installation_1/pleroma-mongooseim.cfg
@@ -0,0 +1,936 @@
+%%%
+%%% ejabberd configuration file
+%%%
+%%%'
+
+%%% The parameters used in this configuration file are explained in more detail
+%%% in the ejabberd Installation and Operation Guide.
+%%% Please consult the Guide in case of doubts, it is included with
+%%% your copy of ejabberd, and is also available online at
+%%% http://www.process-one.net/en/ejabberd/docs/
+
+%%% This configuration file contains Erlang terms.
+%%% In case you want to understand the syntax, here are the concepts:
+%%%
+%%% - The character to comment a line is %
+%%%
+%%% - Each term ends in a dot, for example:
+%%% override_global.
+%%%
+%%% - A tuple has a fixed definition, its elements are
+%%% enclosed in {}, and separated with commas:
+%%% {loglevel, 4}.
+%%%
+%%% - A list can have as many elements as you want,
+%%% and is enclosed in [], for example:
+%%% [http_poll, web_admin, tls]
+%%%
+%%% Pay attention that list elements are delimited with commas,
+%%% but no comma is allowed after the last list element. This will
+%%% give a syntax error unlike in more lenient languages (e.g. Python).
+%%%
+%%% - A keyword of ejabberd is a word in lowercase.
+%%% Strings are enclosed in "" and can contain spaces, dots, ...
+%%% {language, "en"}.
+%%% {ldap_rootdn, "dc=example,dc=com"}.
+%%%
+%%% - This term includes a tuple, a keyword, a list, and two strings:
+%%% {hosts, ["jabber.example.net", "im.example.com"]}.
+%%%
+%%% - This config is preprocessed during release generation by a tool which
+%%% interprets double curly braces as substitution markers, so avoid this
+%%% syntax in this file (though it's valid Erlang).
+%%%
+%%% So this is OK (though arguably looks quite ugly):
+%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }.
+%%%
+%%% And I can't give an example of what's not OK exactly because
+%%% of this rule.
+%%%
+
+
+%%%. =======================
+%%%' OVERRIDE STORED OPTIONS
+
+%%
+%% Override the old values stored in the database.
+%%
+
+%%
+%% Override global options (shared by all ejabberd nodes in a cluster).
+%%
+%%override_global.
+
+%%
+%% Override local options (specific for this particular ejabberd node).
+%%
+%%override_local.
+
+%%
+%% Remove the Access Control Lists before new ones are added.
+%%
+%%override_acls.
+
+
+%%%. =========
+%%%' DEBUGGING
+
+%%
+%% loglevel: Verbosity of log files generated by ejabberd.
+%% 0: No ejabberd log at all (not recommended)
+%% 1: Critical
+%% 2: Error
+%% 3: Warning
+%% 4: Info
+%% 5: Debug
+%%
+{loglevel, 3}.
+
+%%%. ================
+%%%' SERVED HOSTNAMES
+
+%%
+%% hosts: Domains served by ejabberd.
+%% You can define one or several, for example:
+%% {hosts, ["example.net", "example.com", "example.org"]}.
+%%
+{hosts, ["pleroma.soykaf.com"] }.
+
+%%
+%% route_subdomains: Delegate subdomains to other XMPP servers.
+%% For example, if this ejabberd serves example.org and you want
+%% to allow communication with an XMPP server called im.example.org.
+%%
+%%{route_subdomains, s2s}.
+
+
+%%%. ===============
+%%%' LISTENING PORTS
+
+%%
+%% listen: The ports ejabberd will listen on, which service each is handled
+%% by and what options to start it with.
+%%
+{listen,
+ [
+ %% BOSH and WS endpoints over HTTP
+ { 5280, ejabberd_cowboy, [
+ {num_acceptors, 10},
+ {transport_options, [{max_connections, 1024}]},
+ {modules, [
+
+ {"_", "/http-bind", mod_bosh},
+ {"_", "/ws-xmpp", mod_websockets, [{ejabberd_service, [
+ {access, all},
+ {shaper_rule, fast},
+ {ip, {127, 0, 0, 1}},
+ {password, "secret"}]}
+ %% Uncomment to enable connection dropping or/and server-side pings
+ %{timeout, 600000}, {ping_rate, 2000}
+ ]}
+ %% Uncomment to serve static files
+ %{"_", "/static/[...]", cowboy_static,
+ % {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]}
+ %},
+
+ %% Example usage of mod_revproxy
+
+ %% {"_", "/[...]", mod_revproxy, [{timeout, 5000},
+ %% % time limit for upstream to respond
+ %% {body_length, 8000000},
+ %% % maximum body size (may be infinity)
+ %% {custom_headers, [{<<"header">>,<<"value">>}]}
+ %% % list of extra headers that are send to upstream
+ %% ]}
+
+ %% Example usage of mod_cowboy
+
+ %% {"_", "/[...]", mod_cowboy, [{http, mod_revproxy,
+ %% [{timeout, 5000},
+ %% % time limit for upstream to respond
+ %% {body_length, 8000000},
+ %% % maximum body size (may be infinity)
+ %% {custom_headers, [{<<"header">>,<<"value">>}]}
+ %% % list of extra headers that are send to upstream
+ %% ]},
+ %% {ws, xmpp, mod_websockets}
+ %% ]}
+ ]}
+ ]},
+
+ %% BOSH and WS endpoints over HTTPS
+ { 5285, ejabberd_cowboy, [
+ {num_acceptors, 10},
+ {transport_options, [{max_connections, 1024}]},
+ {ssl, [{certfile, "priv/ssl/fullchain.pem"}, {keyfile, "priv/ssl/privkey.pem"}, {password, ""}]},
+ {modules, [
+ {"_", "/http-bind", mod_bosh},
+ {"_", "/ws-xmpp", mod_websockets, [
+ %% Uncomment to enable connection dropping or/and server-side pings
+ %{timeout, 600000}, {ping_rate, 60000}
+ ]}
+ %% Uncomment to serve static files
+ %{"_", "/static/[...]", cowboy_static,
+ % {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]}
+ %},
+ ]}
+ ]},
+
+ %% MongooseIM HTTP API it's important to start it on localhost
+ %% or some private interface only (not accessible from the outside)
+ %% At least start it on different port which will be hidden behind firewall
+
+ { {8088, "127.0.0.1"} , ejabberd_cowboy, [
+ {num_acceptors, 10},
+ {transport_options, [{max_connections, 1024}]},
+ {modules, [
+ {"localhost", "/api", mongoose_api_admin, []}
+ ]}
+ ]},
+
+ { 8089 , ejabberd_cowboy, [
+ {num_acceptors, 10},
+ {transport_options, [{max_connections, 1024}]},
+ {protocol_options, [{compress, true}]},
+ {ssl, [{certfile, "priv/ssl/fullchain.pem"}, {keyfile, "priv/ssl/privkey.pem"}, {password, ""}]},
+ {modules, [
+ {"_", "/api/sse", lasse_handler, [mongoose_client_api_sse]},
+ {"_", "/api/messages/[:with]", mongoose_client_api_messages, []},
+ {"_", "/api/contacts/[:jid]", mongoose_client_api_contacts, []},
+ {"_", "/api/rooms/[:id]", mongoose_client_api_rooms, []},
+ {"_", "/api/rooms/[:id]/config", mongoose_client_api_rooms_config, []},
+ {"_", "/api/rooms/:id/users/[:user]", mongoose_client_api_rooms_users, []},
+ {"_", "/api/rooms/[:id]/messages", mongoose_client_api_rooms_messages, []}
+ ]}
+ ]},
+
+ %% Following HTTP API is deprected, the new one abouve should be used instead
+
+ { {5288, "127.0.0.1"} , ejabberd_cowboy, [
+ {num_acceptors, 10},
+ {transport_options, [{max_connections, 1024}]},
+ {modules, [
+ {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics,
+ mongoose_api_users]}]}
+ ]}
+ ]},
+
+ %% If you want dual stack, you have to clone this entire config stanza
+ %% and change the bind to "::"
+ { {5222, "0.0.0.0"}, ejabberd_c2s, [
+
+ %%
+ %% If TLS is compiled in and you installed a SSL
+ %% certificate, specify the full path to the
+ %% file and uncomment this line:
+ %%
+ {certfile, "priv/ssl/both.pem"}, starttls,
+
+ %%{zlib, 10000},
+ %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+ %% {ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"},
+ {access, c2s},
+ {shaper, c2s_shaper},
+ {max_stanza_size, 65536},
+ {protocol_options, ["no_sslv3"]}
+
+ ]},
+
+
+
+ %%
+ %% To enable the old SSL connection method on port 5223:
+ %%
+ %%{5223, ejabberd_c2s, [
+ %% {access, c2s},
+ %% {shaper, c2s_shaper},
+ %% {certfile, "/path/to/ssl.pem"}, tls,
+ %% {max_stanza_size, 65536}
+ %% ]},
+
+ %% If you want dual stack, you have to clone this entire config stanza
+ %% and change the bind to "::"
+ { {5269, "0.0.0.0"}, ejabberd_s2s_in, [
+ {shaper, s2s_shaper},
+ {max_stanza_size, 131072},
+ {protocol_options, ["no_sslv3"]}
+
+ ]}
+
+ %%
+ %% ejabberd_service: Interact with external components (transports, ...)
+ %%
+ ,{8888, ejabberd_service, [
+ {access, all},
+ {shaper_rule, fast},
+ {ip, {127, 0, 0, 1}},
+ {password, "secret"}
+ ]}
+
+ %%
+ %% ejabberd_stun: Handles STUN Binding requests
+ %%
+ %%{ {3478, udp}, ejabberd_stun, []}
+
+ ]}.
+
+%%
+%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
+%% Allowed values are: false optional required required_trusted
+%% You must specify a certificate file.
+%%
+{s2s_use_starttls, optional}.
+%%
+%% s2s_certfile: Specify a certificate file.
+%%
+{s2s_certfile, "priv/ssl/both.pem"}.
+
+%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+%% {s2s_ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}.
+
+%%
+%% domain_certfile: Specify a different certificate for each served hostname.
+%%
+%%{domain_certfile, "example.org", "/path/to/example_org.pem"}.
+%%{domain_certfile, "example.com", "/path/to/example_com.pem"}.
+
+%%
+%% S2S whitelist or blacklist
+%%
+%% Default s2s policy for undefined hosts.
+%%
+{s2s_default_policy, deny }.
+
+%%
+%% Allow or deny communication with specific servers.
+%%
+%%{ {s2s_host, "goodhost.org"}, allow}.
+%%{ {s2s_host, "badhost.org"}, deny}.
+
+{outgoing_s2s_port, 5269 }.
+
+%%
+%% IP addresses predefined for specific hosts to skip DNS lookups.
+%% Ports defined here take precedence over outgoing_s2s_port.
+%% Examples:
+%%
+%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }.
+%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }.
+%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }.
+
+%%
+%% Outgoing S2S options
+%%
+%% Preferred address families (which to try first) and connect timeout
+%% in milliseconds.
+%%
+%%{outgoing_s2s_options, [ipv4, ipv6], 10000}.
+%%
+%%%. ==============
+%%%' SESSION BACKEND
+
+%%{sm_backend, {mnesia, []}}.
+
+%% Requires {redis, global, default, ..., ...} outgoing pool
+%%{sm_backend, {redis, []}}.
+
+{sm_backend, {mnesia, []} }.
+
+
+%%%. ==============
+%%%' AUTHENTICATION
+
+%% Advertised SASL mechanisms
+{sasl_mechanisms, [cyrsasl_plain]}.
+
+%%
+%% auth_method: Method used to authenticate the users.
+%% The default method is the internal.
+%% If you want to use a different method,
+%% comment this line and enable the correct ones.
+%%
+%% {auth_method, internal }.
+{auth_method, http }.
+{auth_opts, [
+ {http, global, auth, [{workers, 50}], [{server, "https://pleroma.soykaf.com"}]},
+ {password_format, plain} % default
+ %% {password_format, scram}
+
+ %% {scram_iterations, 4096} % default
+
+ %%
+ %% For auth_http:
+ %% {basic_auth, "user:password"}
+ %% {path_prefix, "/"} % default
+ %% auth_http requires {http, Host | global, auth, ..., ...} outgoing pool.
+ %%
+ %% For auth_external
+ %%{extauth_program, "/path/to/authentication/script"}.
+ %%
+ %% For auth_jwt
+ %% {jwt_secret_source, "/path/to/file"},
+ %% {jwt_algorithm, "RS256"},
+ %% {jwt_username_key, user}
+ %% For cyrsasl_external
+ %% {authenticate_with_cn, false}
+ {cyrsasl_external, standard}
+ ]}.
+
+%%
+%% Authentication using external script
+%% Make sure the script is executable by ejabberd.
+%%
+%%{auth_method, external}.
+
+%%
+%% Authentication using RDBMS
+%% Remember to setup a database in the next section.
+%%
+%%{auth_method, rdbms}.
+
+%%
+%% Authentication using LDAP
+%%
+%%{auth_method, ldap}.
+%%
+
+%% List of LDAP servers:
+%%{ldap_servers, ["localhost"]}.
+%%
+%% Encryption of connection to LDAP servers:
+%%{ldap_encrypt, none}.
+%%{ldap_encrypt, tls}.
+%%
+%% Port to connect to on LDAP servers:
+%%{ldap_port, 389}.
+%%{ldap_port, 636}.
+%%
+%% LDAP manager:
+%%{ldap_rootdn, "dc=example,dc=com"}.
+%%
+%% Password of LDAP manager:
+%%{ldap_password, "******"}.
+%%
+%% Search base of LDAP directory:
+%%{ldap_base, "dc=example,dc=com"}.
+%%
+%% LDAP attribute that holds user ID:
+%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}.
+%%
+%% LDAP filter:
+%%{ldap_filter, "(objectClass=shadowAccount)"}.
+
+%%
+%% Anonymous login support:
+%% auth_method: anonymous
+%% anonymous_protocol: sasl_anon | login_anon | both
+%% allow_multiple_connections: true | false
+%%
+%%{host_config, "public.example.org", [{auth_method, anonymous},
+%% {allow_multiple_connections, false},
+%% {anonymous_protocol, sasl_anon}]}.
+%%
+%% To use both anonymous and internal authentication:
+%%
+%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}.
+
+
+%%%. ==============
+%%%' OUTGOING CONNECTIONS (e.g. DB)
+
+%% Here you may configure all outgoing connections used by MongooseIM,
+%% e.g. to RDBMS (such as MySQL), Riak or external HTTP components.
+%% Default MongooseIM configuration uses only Mnesia (non-Mnesia extensions are disabled),
+%% so no options here are uncommented out of the box.
+%% This section includes configuration examples; for comprehensive guide
+%% please consult MongooseIM documentation, page "Outgoing connections":
+%% - doc/advanced-configuration/outgoing-connections.md
+%% - https://mongooseim.readthedocs.io/en/latest/advanced-configuration/outgoing-connections/
+
+
+{outgoing_pools, [
+% {riak, global, default, [{workers, 5}], [{address, "127.0.0.1"}, {port, 8087}]},
+% {elastic, global, default, [], [{host, "elastic.host.com"}, {port, 9042}]},
+ {http, global, auth, [{workers, 50}], [{server, "https://pleroma.soykaf.com"}]}
+% {cassandra, global, default, [{workers, 100}], [{servers, [{"server1", 9042}]}, {keyspace, "big_mongooseim"}]},
+% {rdbms, global, default, [{workers, 10}], [{server, {mysql, "server", 3306, "database", "username", "password"}}]}
+]}.
+
+%% More examples that may be added to outgoing_pools list:
+%%
+%% == MySQL ==
+%% {rdbms, global, default, [{workers, 10}],
+%% [{server, {mysql, "server", 3306, "database", "username", "password"}},
+%% {keepalive_interval, 10}]},
+%% keepalive_interval is optional
+
+%% == PostgreSQL ==
+%% {rdbms, global, default, [{workers, 10}],
+%% [{server, {pgsql, "server", "port", "database", "username", "password"}}]},
+
+%% == ODBC (MSSQL) ==
+%% {rdbms, global, default, [{workers, 10}],
+%% [{server, "DSN=mongooseim;UID=mongooseim;PWD=mongooseim"}]},
+
+%% == Elastic Search ==
+%% {elastic, global, default, [], [{host, "elastic.host.com"}, {port, 9042}]},
+
+%% == Riak ==
+%% {riak, global, default, [{workers, 20}], [{address, "127.0.0.1"}, {port, 8087}]},
+
+%% == HTTP ==
+%% {http, global, conn1, [{workers, 50}], [{server, "http://server:8080"}]},
+
+%% == Cassandra ==
+%% {cassandra, global, default, [{workers, 100}],
+%% [
+%% {servers, [
+%% {"cassandra_server1.example.com", 9042},
+%% {"cassandra_server2.example.com", 9042},
+%% {"cassandra_server3.example.com", 9042},
+%% {"cassandra_server4.example.com", 9042}
+%% ]},
+%% {keyspace, "big_mongooseim"}
+%% ]}
+
+%% == Extra options ==
+%%
+%% If you use PostgreSQL, have a large database, and need a
+%% faster but inexact replacement for "select count(*) from users"
+%%
+%%{pgsql_users_number_estimate, true}.
+%%
+%% rdbms_server_type specifies what database is used over the RDBMS layer
+%% Can take values mssql, pgsql, mysql
+%% In some cases (for example for MAM with pgsql) it is required to set proper value.
+%%
+%% {rdbms_server_type, pgsql}.
+
+%%%. ===============
+%%%' TRAFFIC SHAPERS
+
+%%
+%% The "normal" shaper limits traffic speed to 1000 B/s
+%%
+{shaper, normal, {maxrate, 1000}}.
+
+%%
+%% The "fast" shaper limits traffic speed to 50000 B/s
+%%
+{shaper, fast, {maxrate, 50000}}.
+
+%%
+%% This option specifies the maximum number of elements in the queue
+%% of the FSM. Refer to the documentation for details.
+%%
+{max_fsm_queue, 1000}.
+
+%%%. ====================
+%%%' ACCESS CONTROL LISTS
+
+%%
+%% The 'admin' ACL grants administrative privileges to XMPP accounts.
+%% You can put here as many accounts as you want.
+%%
+%{acl, admin, {user, "alice", "localhost"}}.
+%{acl, admin, {user, "a", "localhost"}}.
+
+%%
+%% Blocked users
+%%
+%%{acl, blocked, {user, "baduser", "example.org"}}.
+%%{acl, blocked, {user, "test"}}.
+
+%%
+%% Local users: don't modify this line.
+%%
+{acl, local, {user_regexp, ""}}.
+
+%%
+%% More examples of ACLs
+%%
+%%{acl, jabberorg, {server, "jabber.org"}}.
+%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
+%%{acl, test, {user_regexp, "^test"}}.
+%%{acl, test, {user_glob, "test*"}}.
+
+%%
+%% Define specific ACLs in a virtual host.
+%%
+%%{host_config, "localhost",
+%% [
+%% {acl, admin, {user, "bob-local", "localhost"}}
+%% ]
+%%}.
+
+%%%. ============
+%%%' ACCESS RULES
+
+%% Maximum number of simultaneous sessions allowed for a single user:
+{access, max_user_sessions, [{10, all}]}.
+
+%% Maximum number of offline messages that users can have:
+{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
+
+%% This rule allows access only for local users:
+{access, local, [{allow, local}]}.
+
+%% Only non-blocked users can use c2s connections:
+{access, c2s, [{deny, blocked},
+ {allow, all}]}.
+
+%% For C2S connections, all users except admins use the "normal" shaper
+{access, c2s_shaper, [{none, admin},
+ {normal, all}]}.
+
+%% All S2S connections use the "fast" shaper
+{access, s2s_shaper, [{fast, all}]}.
+
+%% Admins of this server are also admins of the MUC service:
+{access, muc_admin, [{allow, admin}]}.
+
+%% Only accounts of the local ejabberd server can create rooms:
+{access, muc_create, [{allow, local}]}.
+
+%% All users are allowed to use the MUC service:
+{access, muc, [{allow, all}]}.
+
+%% In-band registration allows registration of any possible username.
+%% To disable in-band registration, replace 'allow' with 'deny'.
+{access, register, [{allow, all}]}.
+
+%% By default the frequency of account registrations from the same IP
+%% is limited to 1 account every 10 minutes. To disable, specify: infinity
+{registration_timeout, infinity}.
+
+%% Default settings for MAM.
+%% To set non-standard value, replace 'default' with 'allow' or 'deny'.
+%% Only user can access his/her archive by default.
+%% An online user can read room's archive by default.
+%% Only an owner can change settings and purge messages by default.
+%% Empty list (i.e. `[]`) means `[{deny, all}]`.
+{access, mam_set_prefs, [{default, all}]}.
+{access, mam_get_prefs, [{default, all}]}.
+{access, mam_lookup_messages, [{default, all}]}.
+{access, mam_purge_single_message, [{default, all}]}.
+{access, mam_purge_multiple_messages, [{default, all}]}.
+
+%% 1 command of the specified type per second.
+{shaper, mam_shaper, {maxrate, 1}}.
+%% This shaper is primeraly for Mnesia overload protection during stress testing.
+%% The limit is 1000 operations of each type per second.
+{shaper, mam_global_shaper, {maxrate, 1000}}.
+
+{access, mam_set_prefs_shaper, [{mam_shaper, all}]}.
+{access, mam_get_prefs_shaper, [{mam_shaper, all}]}.
+{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}.
+{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}.
+{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}.
+
+{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}.
+{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}.
+{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}.
+{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}.
+{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}.
+
+%%
+%% Define specific Access Rules in a virtual host.
+%%
+%%{host_config, "localhost",
+%% [
+%% {access, c2s, [{allow, admin}, {deny, all}]},
+%% {access, register, [{deny, all}]}
+%% ]
+%%}.
+
+%%%. ================
+%%%' DEFAULT LANGUAGE
+
+%%
+%% language: Default language used for server messages.
+%%
+{language, "en"}.
+
+%%
+%% Set a different default language in a virtual host.
+%%
+%%{host_config, "localhost",
+%% [{language, "ru"}]
+%%}.
+
+%%%. ================
+%%%' MISCELLANEOUS
+
+{all_metrics_are_global, false }.
+
+%%%. ========
+%%%' SERVICES
+
+%% Unlike modules, services are started per node and provide either features which are not
+%% related to any particular host, or backend stuff which is used by modules.
+%% This is handled by `mongoose_service` module.
+
+{services,
+ [
+ {service_admin_extra, [{submods, [node, accounts, sessions, vcard,
+ roster, last, private, stanza, stats]}]}
+ ]
+}.
+
+%%%. =======
+%%%' MODULES
+
+%%
+%% Modules enabled in all mongooseim virtual hosts.
+%% For list of possible modules options, check documentation.
+%%
+{modules,
+ [
+
+ %% The format for a single route is as follows:
+ %% {Host, Path, Method, Upstream}
+ %%
+ %% "_" can be used as wildcard for Host, Path and Method
+ %% Upstream can be either host (just http(s)://host:port) or uri
+ %% The difference is that host upstreams append whole path while
+ %% uri upstreams append only remainder that follows the matched Path
+ %% (this behaviour is similar to nginx's proxy_pass rules)
+ %%
+ %% Bindings can be used to match certain parts of host or path.
+ %% They will be later overlaid with parts of the upstream uri.
+ %%
+ %% {mod_revproxy,
+ %% [{routes, [{"www.erlang-solutions.com", "/admin", "_",
+ %% "https://www.erlang-solutions.com/"},
+ %% {":var.com", "/:var", "_", "http://localhost:8080/"},
+ %% {":domain.com", "/", "_", "http://localhost:8080/:domain"}]
+ %% }]},
+
+% {mod_http_upload, [
+ %% Set max file size in bytes. Defaults to 10 MB.
+ %% Disabled if value is `undefined`.
+% {max_file_size, 1024},
+ %% Use S3 storage backend
+% {backend, s3},
+ %% Set options for S3 backend
+% {s3, [
+% {bucket_url, "http://s3-eu-west-1.amazonaws.com/konbucket2"},
+% {region, "eu-west-1"},
+% {access_key_id, "AKIAIAOAONIULXQGMOUA"},
+% {secret_access_key, "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"}
+% ]}
+% ]},
+
+ {mod_adhoc, []},
+
+ {mod_disco, [{users_can_see_hidden_services, false}]},
+ {mod_commands, []},
+ {mod_muc_commands, []},
+ {mod_muc_light_commands, []},
+ {mod_last, []},
+ {mod_stream_management, [
+ % default 100
+ % size of a buffer of unacked messages
+ % {buffer_max, 100}
+
+ % default 1 - server sends the ack request after each stanza
+ % {ack_freq, 1}
+
+ % default: 600 seconds
+ % {resume_timeout, 600}
+ ]},
+ %% {mod_muc_light, [{host, "muclight.@HOST@"}]},
+ %% {mod_muc, [{host, "muc.@HOST@"},
+ %% {access, muc},
+ %% {access_create, muc_create}
+ %% ]},
+ %% {mod_muc_log, [
+ %% {outdir, "/tmp/muclogs"},
+ %% {access_log, muc}
+ %% ]},
+ {mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
+ {mod_privacy, []},
+ {mod_blocking, []},
+ {mod_private, []},
+% {mod_private, [{backend, mnesia}]},
+% {mod_private, [{backend, rdbms}]},
+% {mod_register, [
+% %%
+% %% Set the minimum informational entropy for passwords.
+% %%
+% %%{password_strength, 32},
+%
+% %%
+% %% After successful registration, the user receives
+% %% a message with this subject and body.
+% %%
+% {welcome_message, {""}},
+%
+% %%
+% %% When a user registers, send a notification to
+% %% these XMPP accounts.
+% %%
+%
+%
+% %%
+% %% Only clients in the server machine can register accounts
+% %%
+% {ip_access, [{allow, "127.0.0.0/8"},
+% {deny, "0.0.0.0/0"}]},
+%
+% %%
+% %% Local c2s or remote s2s users cannot register accounts
+% %%
+% %%{access_from, deny},
+%
+% {access, register}
+% ]},
+ {mod_roster, []},
+ {mod_sic, []},
+ {mod_vcard, [%{matches, 1},
+%{search, true},
+%{ldap_search_operator, 'or'}, %% either 'or' or 'and'
+%{ldap_binary_search_fields, [<<"PHOTO">>]},
+%% list of binary search fields (as in vcard after mapping)
+{host, "vjud.@HOST@"}
+]},
+ {mod_bosh, []},
+ {mod_carboncopy, []}
+
+ %%
+ %% Message Archive Management (MAM, XEP-0313) for registered users and
+ %% Multi-User chats (MUCs).
+ %%
+
+% {mod_mam_meta, [
+ %% Use RDBMS backend (default)
+% {backend, rdbms},
+
+ %% Do not store user preferences (default)
+% {user_prefs_store, false},
+ %% Store user preferences in RDBMS
+% {user_prefs_store, rdbms},
+ %% Store user preferences in Mnesia (recommended).
+ %% The preferences store will be called each time, as a message is routed.
+ %% That is why Mnesia is better suited for this job.
+% {user_prefs_store, mnesia},
+
+ %% Enables a pool of asynchronous writers. (default)
+ %% Messages will be grouped together based on archive id.
+% {async_writer, true},
+
+ %% Cache information about users (default)
+% {cache_users, true},
+
+ %% Enable archivization for private messages (default)
+% {pm, [
+ %% Top-level options can be overriden here if needed, for example:
+% {async_writer, false}
+% ]},
+
+ %%
+ %% Message Archive Management (MAM) for multi-user chats (MUC).
+ %% Enable XEP-0313 for "muc.@HOST@".
+ %%
+% {muc, [
+% {host, "muc.@HOST@"}
+ %% As with pm, top-level options can be overriden for MUC archive
+% ]},
+%
+ %% Do not use a <stanza-id/> element (by default stanzaid is used)
+% no_stanzaid_element,
+% ]},
+
+
+ %%
+ %% MAM configuration examples
+ %%
+
+ %% Only MUC, no user-defined preferences, good performance.
+% {mod_mam_meta, [
+% {backend, rdbms},
+% {pm, false},
+% {muc, [
+% {host, "muc.@HOST@"}
+% ]}
+% ]},
+
+ %% Only archives for c2c messages, good performance.
+% {mod_mam_meta, [
+% {backend, rdbms},
+% {pm, [
+% {user_prefs_store, mnesia}
+% ]}
+% ]},
+
+ %% Basic configuration for c2c messages, bad performance, easy to debug.
+% {mod_mam_meta, [
+% {backend, rdbms},
+% {async_writer, false},
+% {cache_users, false}
+% ]},
+
+ %% Cassandra archive for c2c and MUC conversations.
+ %% No custom settings supported (always archive).
+% {mod_mam_meta, [
+% {backend, cassandra},
+% {user_prefs_store, cassandra},
+% {muc, [{host, "muc.@HOST@"}]}
+% ]}
+
+% {mod_event_pusher, [
+% {backends, [
+% %%
+% %% Configuration for Amazon SNS notifications.
+% %%
+% {sns, [
+% %% AWS credentials, region and host configuration
+% {access_key_id, "AKIAJAZYHOIPY6A2PESA"},
+% {secret_access_key, "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"},
+% {region, "eu-west-1"},
+% {account_id, "251423380551"},
+% {region, "eu-west-1"},
+% {sns_host, "sns.eu-west-1.amazonaws.com"},
+%
+% %% Messages from this MUC host will be sent to the SNS topic
+% {muc_host, "muc.@HOST@"},
+%
+% %% Plugin module for defining custom message attributes and user identification
+% {plugin_module, mod_event_pusher_sns_defaults},
+%
+% %% Topic name configurations. Removing a topic will disable this specific SNS notification
+% {presence_updates_topic, "user_presence_updated-dev-1"}, %% For presence updates
+% {pm_messages_topic, "user_message_sent-dev-1"}, %% For private chat messages
+% {muc_messages_topic, "user_messagegroup_sent-dev-1"} %% For group chat messages
+%
+% %% Pool options
+% {pool_size, 100}, %% Worker pool size for publishing notifications
+% {publish_retry_count, 2}, %% Retry count in case of publish error
+% {publish_retry_time_ms, 50} %% Base exponential backoff time (in ms) for publish errors
+% ]}
+% ]}
+
+]}.
+
+
+%%
+%% Enable modules with custom options in a specific virtual host
+%%
+%%{host_config, "localhost",
+%% [{ {add, modules},
+%% [
+%% {mod_some_module, []}
+%% ]
+%% }
+%% ]}.
+
+%%%.
+%%%'
+
+%%% $Id$
+
+%%% Local Variables:
+%%% mode: erlang
+%%% End:
+%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker:
+%%%.
diff --git a/docs/installation_1/pleroma.nginx b/docs/installation_1/pleroma.nginx
new file mode 100644
index 0000000..273cfb3
--- /dev/null
+++ b/docs/installation_1/pleroma.nginx
@@ -0,0 +1,109 @@
+# default nginx site config for Pleroma
+#
+# Simple installation instructions:
+# 1. Install your TLS certificate, possibly using Let's Encrypt.
+# 2. Replace 'example.tld' with your instance's domain wherever it appears.
+# 3. Copy this file to /etc/nginx/sites-available/ and then add a symlink to it
+# in /etc/nginx/sites-enabled/ and run 'nginx -s reload' or restart nginx.
+
+proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g
+ inactive=720m use_temp_path=off;
+
+# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
+# and `localhost.` resolves to [::0] on some systems: see issue #930
+upstream phoenix {
+ server 127.0.0.1:4000 max_fails=5 fail_timeout=60s;
+}
+
+server {
+ server_name example.tld;
+
+ listen 80;
+ listen [::]:80;
+
+ # Uncomment this if you need to use the 'webroot' method with certbot. Make sure
+ # that the directory exists and that it is accessible by the webserver. If you followed
+ # the guide, you already ran 'mkdir -p /var/lib/letsencrypt' to create the folder.
+ # You may need to load this file with the ssl server block commented out, run certbot
+ # to get the certificate, and then uncomment it.
+ #
+ # location ~ /\.well-known/acme-challenge {
+ # root /var/lib/letsencrypt/;
+ # }
+ location / {
+ return 301 https://$server_name$request_uri;
+ }
+}
+
+# Enable SSL session caching for improved performance
+ssl_session_cache shared:ssl_session_cache:10m;
+
+server {
+ server_name example.tld;
+
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ ssl_session_timeout 1d;
+ ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
+ ssl_session_tickets off;
+
+ ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
+ ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
+
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+ ssl_prefer_server_ciphers off;
+ # In case of an old server with an OpenSSL version of 1.0.2 or below,
+ # leave only prime256v1 or comment out the following line.
+ ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_buffers 16 8k;
+ gzip_http_version 1.1;
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
+
+ # the nginx default is 1m, not enough for large media uploads
+ client_max_body_size 16m;
+ ignore_invalid_headers off;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ location / {
+ proxy_pass http://phoenix;
+ }
+
+ # Uncomment this if you want notice compatibility routes for frontends like Soapbox.
+ # location ~ ^/@[^/]+/([^/]+)$ {
+ # proxy_pass http://phoenix/notice/$1;
+ # }
+ #
+ # location ~ ^/@[^/]+/posts/([^/]+)$ {
+ # proxy_pass http://phoenix/notice/$1;
+ # }
+ #
+ # location ~ ^/[^/]+/status/([^/]+)$ {
+ # proxy_pass http://phoenix/notice/$1;
+ # }
+
+ location ~ ^/(media|proxy) {
+ proxy_cache pleroma_media_cache;
+ slice 1m;
+ proxy_cache_key $host$uri$is_args$args$slice_range;
+ proxy_set_header Range $slice_range;
+ proxy_cache_valid 200 206 301 304 1h;
+ proxy_cache_lock on;
+ proxy_ignore_client_abort on;
+ proxy_buffering on;
+ chunked_transfer_encoding on;
+ proxy_pass http://phoenix;
+ }
+}
diff --git a/docs/installation_1/pleroma.service b/docs/installation_1/pleroma.service
new file mode 100644
index 0000000..8338228
--- /dev/null
+++ b/docs/installation_1/pleroma.service
@@ -0,0 +1,36 @@
+[Unit]
+Description=Pleroma social network
+After=network.target postgresql.service
+
+[Service]
+ExecReload=/bin/kill $MAINPID
+KillMode=process
+Restart=on-failure
+
+; Name of the user that runs the Pleroma service.
+User=pleroma
+; Declares that Pleroma runs in production mode.
+Environment="MIX_ENV=prod"
+
+; Make sure that all paths fit your installation.
+; Path to the home directory of the user running the Pleroma service.
+Environment="HOME=/var/lib/pleroma"
+; Path to the folder containing the Pleroma installation.
+WorkingDirectory=/opt/pleroma
+; Path to the Mix binary.
+ExecStart=/usr/bin/mix phx.server
+
+; Some security directives.
+; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
+PrivateTmp=true
+; The /home, /root, and /run/user folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to false.
+ProtectHome=true
+; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
+ProtectSystem=full
+; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.
+PrivateDevices=false
+; Drops the sysadmin capability from the daemon.
+CapabilityBoundingSet=~CAP_SYS_ADMIN
+
+[Install]
+WantedBy=multi-user.target
diff --git a/docs/installation_1/pleroma.supervisord b/docs/installation_1/pleroma.supervisord
new file mode 100644
index 0000000..19efffd
--- /dev/null
+++ b/docs/installation_1/pleroma.supervisord
@@ -0,0 +1,21 @@
+; Assumes pleroma is installed in /home/pleroma/pleroma and running as the pleroma user
+; Also assumes mix is in /usr/bin, this might differ on BSDs or niche Linux distros
+; Logs into /home/pleroma/logs
+[program:pleroma]
+command=/usr/bin/mix phx.server
+directory=/home/pleroma/pleroma
+autostart=true
+autorestart=true
+user=pleroma
+environment =
+ MIX_ENV=prod,
+ HOME=/home/pleroma,
+ USER=pleroma,
+ PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/pleroma/bin:%(ENV_PATH)s",
+ PWD=/home/pleroma/pleroma
+stdout_logfile=/home/pleroma/logs/stdout.log
+stdout_logfile_maxbytes=50MB
+stdout_logfile_backups=10
+stderr_logfile=/home/pleroma/logs/stderr.log
+stderr_logfile_maxbytes=50MB
+stderr_logfile_backups=10 \ No newline at end of file
diff --git a/docs/installation_1/pleroma.vcl b/docs/installation_1/pleroma.vcl
new file mode 100644
index 0000000..4eb2f3c
--- /dev/null
+++ b/docs/installation_1/pleroma.vcl
@@ -0,0 +1,127 @@
+# Recommended varnishncsa logging format: '%h %l %u %t "%m %{X-Forwarded-Proto}i://%{Host}i%U%q %H" %s %b "%{Referer}i" "%{User-agent}i"'
+# Please use Varnish 7.0+ for proper Range Requests / Chunked encoding support
+vcl 4.1;
+import std;
+
+backend default {
+ .host = "127.0.0.1";
+ .port = "4000";
+}
+
+# ACL for IPs that are allowed to PURGE data from the cache
+acl purge {
+ "127.0.0.1";
+}
+
+sub vcl_recv {
+ # Redirect HTTP to HTTPS
+ if (std.port(server.ip) != 443) {
+ set req.http.X-Forwarded-Proto = "http";
+ set req.http.x-redir = "https://" + req.http.host + req.url;
+ return (synth(750, ""));
+ } else {
+ set req.http.X-Forwarded-Proto = "https";
+ }
+
+ # Pipe if WebSockets request is coming through
+ if (req.http.upgrade ~ "(?i)websocket") {
+ return (pipe);
+ }
+
+ # Allow purging of the cache
+ if (req.method == "PURGE") {
+ if (!client.ip ~ purge) {
+ return (synth(405,"Not allowed."));
+ }
+ return (purge);
+ }
+}
+
+sub vcl_backend_response {
+ # gzip text content
+ if (beresp.http.content-type ~ "(text|text/css|application/x-javascript|application/javascript)") {
+ set beresp.do_gzip = true;
+ }
+
+ # Retry broken backend responses.
+ if (beresp.status == 503) {
+ set bereq.http.X-Varnish-Backend-503 = "1";
+ return (retry);
+ }
+
+ # Bypass cache for large files
+ # 50000000 ~ 50MB
+ if (std.integer(beresp.http.content-length, 0) > 50000000) {
+ set beresp.uncacheable = true;
+ return (deliver);
+ }
+
+ # Don't cache objects that require authentication
+ if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
+ set beresp.uncacheable = true;
+ return (deliver);
+ }
+
+ # Allow serving cached content for 6h in case backend goes down
+ set beresp.grace = 6h;
+
+ # Do not cache 5xx responses
+ if (beresp.status == 500 || beresp.status == 502 || beresp.status == 503 || beresp.status == 504) {
+ set beresp.uncacheable = true;
+ return (abandon);
+ }
+
+ # Do not cache redirects and errors
+ if ((beresp.status >= 300) && (beresp.status < 500)) {
+ set beresp.uncacheable = true;
+ set beresp.ttl = 30s;
+ return (deliver);
+ }
+}
+
+# The synthetic response for 301 redirects
+sub vcl_synth {
+ if (resp.status == 750) {
+ set resp.status = 301;
+ set resp.http.Location = req.http.x-redir;
+ return (deliver);
+ }
+}
+
+# Ensure WebSockets through the pipe do not close prematurely
+sub vcl_pipe {
+ if (req.http.upgrade) {
+ set bereq.http.upgrade = req.http.upgrade;
+ set bereq.http.connection = req.http.connection;
+ }
+}
+
+sub vcl_backend_fetch {
+ # Be more lenient for slow servers on the fediverse
+ if (bereq.url ~ "^/proxy/") {
+ set bereq.first_byte_timeout = 300s;
+ }
+
+ if (bereq.retries == 0) {
+ # Clean up the X-Varnish-Backend-503 flag that is used internally
+ # to mark broken backend responses that should be retried.
+ unset bereq.http.X-Varnish-Backend-503;
+ } else {
+ if (bereq.http.X-Varnish-Backend-503) {
+ if (bereq.method != "POST" &&
+ std.healthy(bereq.backend) &&
+ bereq.retries <= 4) {
+ # Flush broken backend response flag & try again.
+ unset bereq.http.X-Varnish-Backend-503;
+ } else {
+ return (abandon);
+ }
+ }
+ }
+}
+
+sub vcl_backend_error {
+ # Retry broken backend responses.
+ set bereq.http.X-Varnish-Backend-503 = "1";
+ return (retry);
+}