├── .gitignore ├── .gitmodules ├── src ├── users │ ├── more_button.png │ ├── moderation_01.png │ ├── moderation_02.png │ ├── 07-other-features.md │ ├── 06-fediverse-interaction.md │ ├── 03-votes-and-ranking.md │ ├── 08-history-of-lemmy.md │ ├── 05-censorship-resistance.md │ ├── 04-moderation.md │ ├── 01-getting-started.md │ └── 02-media.md ├── contributors │ ├── rank_algorithm.png │ ├── 01-overview.md │ ├── 06-resources.md │ ├── 07-ranking-algo.md │ ├── 08-plugins.md │ ├── 03-docker-development.md │ ├── 02-local-development.md │ ├── 04-api.md │ └── 05-federation.md ├── administration │ ├── install_ansible.md │ ├── configuration.md │ ├── caddy.md │ ├── on_aws.md │ ├── administration.md │ ├── separate_subdomains.md │ ├── install_docker.md │ ├── first_steps.md │ ├── federation_getting_started.md │ ├── backup_and_restore.md │ ├── theming.md │ ├── prometheus.md │ ├── horizontal_scaling.md │ ├── tor_hidden_service.md │ ├── troubleshooting.md │ └── from_scratch.md ├── SUMMARY.md ├── introduction.md └── code_of_conduct.md ├── book.toml ├── renovate.json ├── README.md ├── .woodpecker.yml └── assets ├── docker-compose.yml └── separate_subdomains ├── nginx └── nginx_internal.conf └── traefik └── compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | include 3 | node_modules 4 | .idea 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lemmy"] 2 | path = lemmy 3 | url = https://github.com/LemmyNet/lemmy.git 4 | -------------------------------------------------------------------------------- /src/users/more_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/src/users/more_button.png -------------------------------------------------------------------------------- /src/users/moderation_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/src/users/moderation_01.png -------------------------------------------------------------------------------- /src/users/moderation_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/src/users/moderation_02.png -------------------------------------------------------------------------------- /src/contributors/rank_algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/src/contributors/rank_algorithm.png -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Felix Ableitner"] 3 | src = "src" 4 | 5 | [output.html] 6 | git-repository-url = "https://github.com/LemmyNet/lemmy-docs" 7 | -------------------------------------------------------------------------------- /src/administration/install_ansible.md: -------------------------------------------------------------------------------- 1 | # Install with Ansible 2 | 3 | Follow the instructions on the [Lemmy-Ansible](https://github.com/LemmyNet/lemmy-ansible) repo. 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"], 4 | "schedule": ["every weekend"], 5 | "automerge": true 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lemmy Documentation 2 | 3 | Documentation for the Lemmy project 4 | 5 | ## Building 6 | 7 | Follow the [mdbook instructions](https://rust-lang.github.io/mdBook/guide/installation.html). 8 | 9 | ## Formatting 10 | 11 | To format your markdown files, install [prettier](https://prettier.io), then run `prettier -w src` 12 | -------------------------------------------------------------------------------- /src/administration/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | The configuration is based on the file config.hjson, which is located by default at `config/config.hjson`, or `lemmy.hjson` when using Docker. You can set a different path with the environment variable `LEMMY_CONFIG_LOCATION`. 4 | 5 | ## Full config with default values 6 | 7 | ```hjson 8 | {{#include ../../lemmy/config/defaults.hjson}} 9 | ``` 10 | 11 | ## Lemmy-UI configuration 12 | 13 | Lemmy-UI can be configured using environment variables, detailed in its [README](https://github.com/LemmyNet/lemmy-ui#readme). 14 | -------------------------------------------------------------------------------- /src/administration/caddy.md: -------------------------------------------------------------------------------- 1 | # Using Caddy as reverse proxy 2 | 3 | If you prefer to use Caddy instead of Nginx - you could use this template to fit into your needs: 4 | 5 | ``` 6 | (caddy-common) { 7 | encode gzip 8 | header { 9 | -Server 10 | Strict-Transport-Security "max-age=31536000; include-subdomains;" 11 | X-XSS-Protection "1; mode=block" 12 | X-Frame-Options "DENY" 13 | X-Content-Type-Options nosniff 14 | Referrer-Policy no-referrer-when-downgrade 15 | X-Robots-Tag "none" 16 | } 17 | } 18 | 19 | lemmy-site.com { 20 | import caddy-common 21 | reverse_proxy http://lemmy_lemmy-ui_1:1234 22 | } 23 | 24 | @lemmy { 25 | path /api/* 26 | path /pictrs/* 27 | path /feeds/* 28 | path /nodeinfo/* 29 | path /.well-known/* 30 | } 31 | 32 | @lemmy-hdr { 33 | header Accept application/* 34 | } 35 | 36 | handle @lemmy { 37 | reverse_proxy http://lemmy_lemmy_1:8536 38 | } 39 | 40 | handle @lemmy-hdr { 41 | reverse_proxy http://lemmy_lemmy_1:8536 42 | } 43 | 44 | @lemmy-post { 45 | method POST 46 | } 47 | 48 | handle @lemmy-post { 49 | reverse_proxy http://lemmy_lemmy_1:8536 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /.woodpecker.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | prepare_repo: 3 | image: alpine:3 4 | commands: 5 | - apk add git 6 | - git submodule init 7 | - git submodule update --recursive --remote 8 | when: 9 | - event: pull_request 10 | 11 | check_formatting: 12 | image: tmknom/prettier 13 | commands: 14 | - prettier -c src 15 | when: 16 | - event: pull_request 17 | 18 | check_typos: 19 | image: alpine:3 20 | commands: 21 | - wget -O typos.tar.gz https://github.com/crate-ci/typos/releases/download/v1.14.12/typos-v1.14.12-x86_64-unknown-linux-musl.tar.gz 22 | - tar -xzf typos.tar.gz 23 | - ./typos src 24 | when: 25 | - event: pull_request 26 | 27 | check_documentation_build: 28 | image: alpine:3 29 | commands: 30 | - apk add bash curl git 31 | - wget -O mdbook.tar.gz https://github.com/rust-lang/mdBook/releases/download/v0.4.30/mdbook-v0.4.30-x86_64-unknown-linux-musl.tar.gz 32 | - tar -xzf mdbook.tar.gz 33 | - ls -la mdbook 34 | - ./mdbook build . 35 | when: 36 | - event: pull_request 37 | 38 | dead_links: 39 | image: becheran/mlc:1.2.0 40 | commands: 41 | - mlc src/en --offline --ignore-links "*localhost*" --ignore-links "*127.0.0.1*" --ignore-links /api 42 | when: 43 | - event: pull_request 44 | -------------------------------------------------------------------------------- /src/administration/on_aws.md: -------------------------------------------------------------------------------- 1 | # Install on AWS 2 | 3 | > ⚠️ **Disclaimer:** this installation method is not recommended by the Lemmy developers. If you have any problems, you need to solve them yourself or ask the respective authors. If you notice any Lemmy bugs on an instance installed like this, please mention it in the bug report. 4 | 5 | ## Lemmy AWS CDK 6 | 7 | This contains the necessary infrastructure definitions to deploy [Lemmy](https://github.com/LemmyNet/lemmy) 8 | to AWS using their [Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/home.html). 9 | 10 | ### Included: 11 | 12 | - ECS Fargate cluster 13 | - Lemmy-UI 14 | - Lemmy 15 | - Pictrs 16 | - CloudFront CDN 17 | - EFS storage for image uploads 18 | - Aurora Serverless Postgres DB 19 | - Bastion VPC host 20 | - Load balancers for Lemmy 21 | - DNS records for your site 22 | 23 | ## Quickstart 24 | 25 | Clone the [Lemmy-CDK](https://github.com/jetbridge/lemmy-cdk). 26 | 27 | Clone [Lemmy](https://github.com/LemmyNet/lemmy) and [Lemmy-UI](https://github.com/LemmyNet/lemmy-ui) to the directory above this. 28 | 29 | ```shell 30 | cp example.env.local .env.local 31 | # edit .env.local 32 | ``` 33 | 34 | You should edit .env.local with your site settings. 35 | 36 | ```shell 37 | npm install -g aws-cdk 38 | npm install 39 | cdk bootstrap 40 | cdk deploy 41 | ``` 42 | 43 | ## Cost 44 | 45 | This is _not_ the cheapest way to run Lemmy. The Serverless Aurora DB can run you ~$90/mo if it doesn't go to sleep. 46 | 47 | ## Useful CDK commands 48 | 49 | - `npm run build` compile typescript to js 50 | - `npm run watch` watch for changes and compile 51 | - `npm run test` perform the jest unit tests 52 | - `cdk deploy` deploy this stack to your default AWS account/region 53 | - `cdk diff` compare deployed stack with current state 54 | - `cdk synth` emits the synthesized CloudFormation template 55 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](introduction.md) 4 | 5 | # For Users 6 | 7 | - [Getting Started](users/01-getting-started.md) 8 | - [Media](users/02-media.md) 9 | - [Votes and Ranking](users/03-votes-and-ranking.md) 10 | - [Moderation](users/04-moderation.md) 11 | - [Censorship Resistance](users/05-censorship-resistance.md) 12 | - [Fediverse Interaction](users/06-fediverse-interaction.md) 13 | - [Other Features](users/07-other-features.md) 14 | - [History of Lemmy](users/08-history-of-lemmy.md) 15 | 16 | # For Admins 17 | 18 | - [Administration](administration/administration.md) 19 | - [Install with Docker](administration/install_docker.md) 20 | - [Install with Ansible](administration/install_ansible.md) 21 | - [Install from Scratch](administration/from_scratch.md) 22 | - [Install on AWS](administration/on_aws.md) 23 | - [First Steps](administration/first_steps.md) 24 | - [Configuration](administration/configuration.md) 25 | - [Theming Guide](administration/theming.md) 26 | - [Getting started with Federation](administration/federation_getting_started.md) 27 | - [Troubleshooting](administration/troubleshooting.md) 28 | - [Backup and Restore](administration/backup_and_restore.md) 29 | - [Using Caddy as a reverse proxy](administration/caddy.md) 30 | - [Lemmy on Separate Subdomains](administration/separate_subdomains.md) 31 | - [Running a Tor Hidden Service](administration/tor_hidden_service.md) 32 | - [Prometheus Metrics](administration/prometheus.md) 33 | - [Horizontal Scaling](administration/horizontal_scaling.md) 34 | 35 | # For Contributors 36 | 37 | - [Overview](contributors/01-overview.md) 38 | - [Local Development](contributors/02-local-development.md) 39 | - [Docker Development](contributors/03-docker-development.md) 40 | - [API](contributors/04-api.md) 41 | - [Federation](contributors/05-federation.md) 42 | - [Resources](contributors/06-resources.md) 43 | - [Ranking Algorithm](contributors/07-ranking-algo.md) 44 | - [Plugins](contributors/08-plugins.md) 45 | 46 | # Code of Conduct 47 | 48 | - [Code of Conduct](code_of_conduct.md) 49 | -------------------------------------------------------------------------------- /src/administration/administration.md: -------------------------------------------------------------------------------- 1 | # Administration 2 | 3 | Information for Lemmy instance admins, and those who want to run a server. 4 | 5 | If you have any problems in the installation, you can ask for help in [!lemmy_support](https://lemmy.ml/c/lemmy_support) or [Matrix](https://matrix.to/#/#lemmy-support-general:discuss.online). Do not open Github issues for support. 6 | 7 | ## Install 8 | 9 | ### Official/Supported methods 10 | 11 | Lemmy has two primary installation methods: 12 | 13 | - [Manually with Docker](install_docker.md) 14 | - [Automated with Ansible](install_ansible.md) 15 | 16 | We recommend using Ansible, because it simplifies the installation and also makes updating easier. 17 | 18 | Lemmy uses roughly 150 MB of RAM in the default Docker installation. CPU usage is negligible. 19 | 20 | ### Managed Hostings 21 | 22 | - [Elestio](https://elest.io/open-source/lemmy) 23 | - [Communick](https://communick.com/lemmy) 24 | 25 | ### Other installation methods 26 | 27 | > ⚠️ **Under your own risk.** 28 | 29 | In some cases, it might be necessary to use different installation methods. 30 | 31 | - [From Scratch](from_scratch.md) 32 | - [YunoHost](https://install-app.yunohost.org/?app=lemmy) ([source code](https://github.com/YunoHost-Apps/lemmy_ynh)) 33 | - [On Amazon Web Services (AWS)](on_aws.md) 34 | - [Nomad](https://www.nomadproject.io/) ([see this external repo for examples](https://github.com/Cottand/lemmy-on-nomad-examples)) 35 | 36 | ### You could use any other reverse proxy 37 | 38 | An Example [Caddy configuration](caddy.md). 39 | 40 | ## Lemmy components 41 | 42 | ### Lemmy-ui 43 | 44 | Lemmy-ui is the main frontend for Lemmy. It consists of an expressjs based server-side process (necessary for SSR) and client code which run in the browser. It does not use a lot of resources and will happily run on quite low powered servers. 45 | 46 | ### Lemmy_server 47 | 48 | Lemmy_server is the backend process, which handles: 49 | 50 | - Incoming HTTP requests (both from Lemmy clients and incoming federation from other servers) 51 | - Outgoing federation 52 | - Scheduled tasks (most notably, constant hot rank calculations, which keep the front page fresh) 53 | 54 | ### Pict-rs 55 | 56 | Pict-rs is a service which does image processing. It handles user-uploaded images as well as downloading thumbnails for external images. 57 | -------------------------------------------------------------------------------- /src/users/07-other-features.md: -------------------------------------------------------------------------------- 1 | # Other Features 2 | 3 | ## Theming 4 | 5 | Users can choose between a number of built-in color themes. Admins can also provide additional themes and set them as default. 6 | 7 | ## Easy to Install, Low Hardware Requirements 8 | 9 | Lemmy is written in Rust, which is an extremely fast language. This is why it has very low hardware requirements. It can easily run on a Raspberry Pi or similar low-powered hardware. This makes it easy to administrate and keeps costs low. 10 | 11 | ## Backup of User Settings 12 | 13 | Users can create a backup of their account data on the `/settings` page and store it locally. This can help in case the Lemmy instance goes down or the account gets banned. The backup includes settings like display name, bio, sort options as well as followed communities, saved posts, blocked communities and more. After registering on a new Lemmy instance, the backup file can be imported in the same place to restore the data. Restoring overwrites all the existing settings like bio and sort options. Follows and blocks on the other hand are additive, existing follows or blocks are kept unchanged. It is save to edit the backup file with a text editor to remove specific lines and avoid importing them (just be sure it is valid json before uploading). 14 | 15 | ## Language Tags 16 | 17 | Lemmy instances and communities can specify which languages can be used for posting. Consider an instance aimed at Spanish users, it would limit the posting language to Spanish so that other languages can't be used. Or an international instance which only allows languages that the admin team understands. Community languages work in the same way, and are restricted to a subset of the instance languages. By default, all languages are allowed (including _undefined_). 18 | 19 | Users can also specify which languages they speak, and will only see content in those languages. Lemmy tries to smartly select a default language for new posts if possible. Otherwise you have to specify the language manually. 20 | 21 | ## Lemmy as a Blog 22 | 23 | Lemmy can also function as a blogging platform. Doing this is as simple as creating a community and enabling the option "Only moderators can post to this community". Now only you and other people that you invite can create posts, while everyone else can comment. Like any Lemmy community, it is also possible to follow from other Fediverse platforms and over RSS. For advanced usage, it is even possible to use the API and create a different frontend which looks more blog-like. 24 | -------------------------------------------------------------------------------- /src/contributors/01-overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Lemmy is an open source project and relies on community contributions to get better. Development happens mainly in the [Lemmy Github repositories](https://github.com/LemmyNet). Communication is done over [Matrix](https://matrix.to/#/#activitypub-community:codelutin.com). 4 | 5 | These are the main repositories which are relevant for contributors: 6 | 7 | - [lemmy](https://github.com/LemmyNet/lemmy): The backend which is the core of Lemmy. It implements SQL queries, provides the API and handles ActivityPub federation. Additionally it sends emails and provides RSS feeds. Written in Rust with actix-web and diesel. The issue tracker is also used for general enhancements which affect multiple repositories. 8 | - [lemmy-ui](https://github.com/LemmyNet/lemmy-ui): The main frontend for Lemmy. It provides the user interface that you see when viewing a Lemmy instance. Written in Typescript and CSS with the Inferno framework. 9 | - [lemmy-ansible](https://github.com/LemmyNet/lemmy-ansible): Automated installation method which is recommended for users without technical knowledge. 10 | - [joinlemmy-site](https://github.com/LemmyNet/joinlemmy-site): Source code for the official project website [join-lemmy.org](https://join-lemmy.org/). Landing page for new users which includes general information and a list of instances. 11 | - [lemmy-js-client](https://github.com/LemmyNet/lemmy-js-client): Client for the Lemmy API which is used by lemmy-ui. Can also be used by other projects to get started more easily. 12 | - [activitypub-federation-rust](https://github.com/LemmyNet/activitypub-federation-rust): High-level framework for ActivityPub federation in Rust. Was originally part of the Lemmy code, but was extracted and made more generic so that other projects can use it too. 13 | 14 | There are many different ways to contribute, depending on your skills and interests: 15 | 16 | ### Reporting issues 17 | 18 | You may notice different problems or potential improvements when using Lemmy. You can report them in the relevant repository. Make sure to first search the issue tracker to avoid duplicates. When creating the issue, fill in the template and include as much relevant information as possible. 19 | 20 | ### Translating 21 | 22 | Use [Weblate](https://weblate.join-lemmy.org/projects/lemmy/) to help translate Lemmy into different languages. 23 | 24 | ### Programming and Design 25 | 26 | You can open a pull request to make changes to Lemmy. In case of bigger contributions it is recommended to discuss it in an issue first. See the next sections for instructions to compile and develop the projects. 27 | 28 | ### Donating 29 | 30 | Besides contributing with your time, you can also consider [making a donation](https://join-lemmy.org/donate) to support the developers. 31 | -------------------------------------------------------------------------------- /src/users/06-fediverse-interaction.md: -------------------------------------------------------------------------------- 1 | # Fediverse interaction 2 | 3 | As mentioned in the introduction, Lemmy users and non-Lemmy users from the fediverse can communicate between each other, too. The way Lemmy users' activity is handled by non-Lemmy instances depends on the software running on the latter. If you are interested in that, you should read the documentation of the software run by the non-Lemmy instance in question. Here, we only focus on how non-Lemmy users' activity is handled by Lemmy instances. There are only a few things to keep in mind: 4 | 5 | - Many fediverse-compatible social media have no notion of "group". These display Lemmy communities as single users, e.g. `@meta@sopuli.xyz` rather than `!meta@sopuli.xyz`. Just open the community from your non-Lemmy instance to check the correct handle. 6 | - Users of link aggregators like Lemmy tend not to use hashtags and mentions, so consider limiting yourself to the bare minimum when posting or commenting, even if the rules of the Lemmy community/instance in question do not mention it. 7 | 8 | ## Follow a Lemmy community 9 | 10 | You can freely follow any Lemmy community from non-Lemmy software. 11 | 12 | ## Post in a Lemmy community 13 | 14 | To post in a Lemmy community from a non-Lemmy fediverse account, simply create a post following these rules: 15 | 16 | - If your non-Lemmy software does not feature a "title" field for posts, the first line (i.e., the content before the first newline character) should be the desired title for your Lemmy post. Only its first 100 characters will be shown, while the rest will be ignored. 17 | - The post visibility must be "Public" or similar. 18 | - You must mention the Lemmy community's full handle. 19 | - Any other mentions should be placed after the one involving the target Lemmy community. 20 | - Media should have alt text, as this is the same alt text that will be shown in Lemmy. 21 | 22 | If your post follows these rules, it should be automatically reposted by the Lemmy community almost immediately. Unlike media, links cannot be attached, but you can simply insert them within the content of your post. 23 | 24 | ## Comment under a Lemmy post 25 | 26 | Comments to a Lemmy post (or to one of its comments) will be visible on Lemmy. 27 | 28 | ## Troubleshooting 29 | 30 | If your message does not appear in the community, you should: 31 | 32 | - Wait a few minutes. 33 | - Double-check the rules mentioned above. 34 | - Ensure the Lemmy instance has not defederated from your non-Lemmy instance (or the other way around). For example, the Lemmy administrator might have disabled federation with all instances (except for a limited whitelist), or they could have blacklisted the non-Lemmy instance specifically. To check this, Lemmy offers an "Instances" button in its footer. The software run by the non-Lemmy instance probably offers a similar functionality. 35 | -------------------------------------------------------------------------------- /assets/docker-compose.yml: -------------------------------------------------------------------------------- 1 | x-logging: &default-logging 2 | driver: "json-file" 3 | options: 4 | max-size: "50m" 5 | max-file: "4" 6 | 7 | services: 8 | proxy: 9 | image: nginx:1-alpine 10 | ports: 11 | # Listen for outside connections on port 10633. You can freely change the left-side 12 | # number to a different port, eg using port 80 if you don't need a reverse proxy. 13 | - "10633:8536" 14 | volumes: 15 | - ./nginx_internal.conf:/etc/nginx/nginx.conf:ro,Z 16 | - ./proxy_params:/etc/nginx/proxy_params:ro,Z 17 | restart: always 18 | logging: *default-logging 19 | depends_on: 20 | - pictrs 21 | - lemmy-ui 22 | 23 | lemmy: 24 | image: dessalines/lemmy:0.19.14 25 | hostname: lemmy 26 | restart: always 27 | logging: *default-logging 28 | environment: 29 | - RUST_LOG="warn" 30 | volumes: 31 | - ./lemmy.hjson:/config/config.hjson:Z 32 | depends_on: 33 | - postgres 34 | - pictrs 35 | 36 | lemmy-ui: 37 | image: dessalines/lemmy-ui:0.19.14 38 | environment: 39 | - LEMMY_UI_LEMMY_INTERNAL_HOST=lemmy:8536 40 | - LEMMY_UI_LEMMY_EXTERNAL_HOST={{ domain }} 41 | - LEMMY_UI_HTTPS=true 42 | volumes: 43 | - ./volumes/lemmy-ui/extra_themes:/app/extra_themes 44 | depends_on: 45 | - lemmy 46 | restart: always 47 | logging: *default-logging 48 | 49 | pictrs: 50 | image: asonix/pictrs:0.5.19 51 | # this needs to match the pictrs url in lemmy.hjson 52 | hostname: pictrs 53 | # we can set options to pictrs like this, here we set max. image size and forced format for conversion 54 | # entrypoint: /sbin/tini -- /usr/local/bin/pict-rs -p /mnt -m 4 --image-format webp 55 | environment: 56 | - PICTRS_OPENTELEMETRY_URL=http://otel:4137 57 | - PICTRS__SERVER__API_KEY={{ postgres_password }} 58 | - RUST_BACKTRACE=full 59 | - PICTRS__MEDIA__VIDEO__VIDEO_CODEC=vp9 60 | - PICTRS__MEDIA__ANIMATION__MAX_WIDTH=256 61 | - PICTRS__MEDIA__ANIMATION__MAX_HEIGHT=256 62 | - PICTRS__MEDIA__ANIMATION__MAX_FRAME_COUNT=400 63 | user: 991:991 64 | volumes: 65 | - ./volumes/pictrs:/mnt:Z 66 | restart: always 67 | logging: *default-logging 68 | 69 | postgres: 70 | image: pgautoupgrade/pgautoupgrade:18-alpine 71 | hostname: postgres 72 | environment: 73 | - POSTGRES_USER=lemmy 74 | - POSTGRES_PASSWORD={{ postgres_password }} 75 | - POSTGRES_DB=lemmy 76 | shm_size: 1g 77 | volumes: 78 | - ./volumes/postgres:/var/lib/postgresql:Z 79 | - ./customPostgresql.conf:/etc/postgresql.conf 80 | restart: always 81 | logging: *default-logging 82 | 83 | postfix: 84 | image: mwader/postfix-relay 85 | environment: 86 | - POSTFIX_myhostname={{ domain }} 87 | restart: "always" 88 | logging: *default-logging 89 | -------------------------------------------------------------------------------- /src/contributors/06-resources.md: -------------------------------------------------------------------------------- 1 | # Resources 2 | 3 | ## Rust 4 | 5 | - [Diesel to Postgres data types](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html) 6 | - [helpful diesel examples](http://siciarz.net/24-days-rust-diesel/) 7 | - [Rust JWT](https://github.com/Keats/jsonwebtoken) 8 | - [Rust docker build](https://shaneutt.com/blog/rust-fast-small-docker-image-builds/) 9 | 10 | ## Typescript 11 | 12 | - [Markdown-it](https://github.com/markdown-it/markdown-it) 13 | - [Sticky Sidebar](https://stackoverflow.com/questions/38382043/how-to-use-css-position-sticky-to-keep-a-sidebar-visible-with-bootstrap-4/49111934) 14 | - [RXJS websocket](https://stackoverflow.com/questions/44060315/reconnecting-a-websocket-in-angular-and-rxjs/44067972#44067972) 15 | - [Simple markdown editor](https://github.com/sparksuite/simplemde-markdown-editor) 16 | - [Zurb mentions](https://github.com/zurb/tribute) 17 | - [TippyJS](https://github.com/atomiks/tippyjs) 18 | 19 | ### Trees 20 | 21 | - [Hierarchical tree building javascript](https://stackoverflow.com/a/40732240/1655478) 22 | - [Recursive query for adjacency list for nested comments](https://stackoverflow.com/questions/192220/what-is-the-most-efficient-elegant-way-to-parse-a-flat-table-into-a-tree/192462#192462) 23 | 24 | ## Sorting 25 | 26 | - [Hot sorting discussion](https://meta.stackexchange.com/questions/11602/what-formula-should-be-used-to-determine-hot-questions) [2](https://archive.is/x0AJT) 27 | 28 | ## SQL 29 | 30 | - [SQL function indexes](https://sorentwo.com/2013/12/30/let-postgres-do-the-work.html) 31 | 32 | ## ActivityPub Resources 33 | 34 | ### Official Documents 35 | 36 | - [ActivityPub standard](https://www.w3.org/TR/activitypub/) 37 | - [Activitypub vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/) 38 | 39 | ### Explanations 40 | 41 | - [ActivityPub - one protocol to rule them all?](https://schub.io/blog/2018/02/01/activitypub-one-protocol-to-rule-them-all.html) 42 | - [A highly opinionated guide to learning about ActivityPub](https://tinysubversions.com/notes/reading-activitypub/) 43 | - [Activitypub implementers guide](https://socialhub.activitypub.rocks/t/draft-guide-for-new-activitypub-implementers/479) 44 | - [Mastodon Blog: How to implement a basic ActivityPub server](https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/) 45 | - [Mastodon Blog: Implementing an ActivityPub inbox](https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/) 46 | - [Data storage questions](https://socialhub.activitypub.rocks/t/data-storage-questions/579) 47 | - [Activitypub as it has been understood](https://flak.tedunangst.com/post/ActivityPub-as-it-has-been-understood) 48 | 49 | ### Examples and Libraries 50 | 51 | - [activitypub-federation-rust library](https://crates.io/crates/activitypub-federation) 52 | - [ActivityPub example server](https://github.com/tOkeshu/activitypub-example) 53 | - [ActivityStreams crate](https://docs.rs/activitystreams/) 54 | - [HTTP Signatures crate](https://git.asonix.dog/asonix/http-signature-normalization) 55 | -------------------------------------------------------------------------------- /src/contributors/07-ranking-algo.md: -------------------------------------------------------------------------------- 1 | # Ranking Algorithm 2 | 3 | ## Goals 4 | 5 | - During the day, new posts and comments should be near the top, so they can be voted on. 6 | - After a day or so, the time factor should go away. 7 | - Use a log scale, since votes tend to snowball, and so the first 10 votes are just as important as the next hundred. 8 | 9 | ## Implementations 10 | 11 | ### Reddit 12 | 13 | Does not take the lifetime of the thread into account, [giving early comments an overwhelming advantage over later ones,](https://minimaxir.com/2016/11/first-comment/) with the effect being even worse in small communities. New comments pool at the bottom of the thread, effectively killing off discussion and making each thread a race to comment early. This lowers the quality of conversation and rewards comments that are repetitive and spammy. 14 | 15 | ### Hacker News 16 | 17 | While far superior to Reddit's implementation for its decay of scores over time, [Hacker News' ranking algorithm](https://medium.com/hacking-and-gonzo/how-hacker-news-ranking-algorithm-works-1d9b0cf2c08d) does not use a logarithmic scale for scores. 18 | 19 | ### Lemmy 20 | 21 | Counterbalances the snowballing effect of votes over time with a logarithmic scale. Negates the inherent advantage of early comments while still ensuring that votes still matter in the long-term, not nuking older popular comments. 22 | 23 | ``` 24 | Rank = ScaleFactor * log(Max(1, 3 + Score)) / (Time + 2)^Gravity 25 | Scaled_Rank = Rank / log(2 + Users_Active_Month) 26 | 27 | Score = Upvotes - Downvotes 28 | Time = time since submission (in hours) 29 | Gravity = Decay gravity, 1.8 is default 30 | Users_Active_Month = The number of users in a given community who have posted / commented / voted in the last month. 31 | ``` 32 | 33 | - Lemmy uses the same `Rank` algorithm above, in three sorts: `Active`, `Hot`, and `Scaled`. 34 | - `Active` uses the post votes, and latest comment time (limited to two days). 35 | - `Hot` uses the post votes, and the post published time. 36 | - `Scaled` is similar to `Hot`, but gives a boost to smaller / less active communities. 37 | - Use Max(1, score) to make sure all comments are affected by time decay. 38 | - Add 3 to the score, so that everything that has less than 3 downvotes will seem new. Otherwise all new comments would stay at zero, near the bottom. 39 | - The sign and abs of the score are necessary for dealing with the log of negative scores. 40 | - A scale factor of 10k gets the rank in integer form. 41 | 42 | A plot of rank over 24 hours, of scores of 1, 5, 10, 100, 1000, with a scale factor of 10k. 43 | 44 | ![rank_algorithm.png](rank_algorithm.png) 45 | 46 | #### Active User counts 47 | 48 | Lemmy also shows counts of _active users_ for your site, and its communities. These are counted within the last `day`, `week`, `month`, and `half year`, and are cached on starting up lemmy, and every hour. 49 | 50 | An active user is someone who has posted, commented, or voted on our instance or community within the last given time frame. For site counts, only local users are counted. For community counts, federated users are included. 51 | -------------------------------------------------------------------------------- /src/administration/separate_subdomains.md: -------------------------------------------------------------------------------- 1 | # Lemmy on Separate Subdomains 2 | 3 | This guide details hosting the Lemmy backend and frontend on different domains. This is useful, for example, if you want users to have a webfinger (user handle) without a subdomain, but want to host services on the same domain. 4 | 5 | As a general overview, this works by handling federation requests on the short domain and forwarding them to the Lemmy backend. API and frontend requests however are handled on the long domain. 6 | 7 | ## Placeholders 8 | 9 | - `short.domain` represents the shortened domain you would like to use for user handles. 10 | - `sub.longerdomain.com` represents the domain where the UI will be served. 11 | - The `proxy` network is the configured Traefik network, and is only referenced in the Traefik configuration. Please see the [Traefik documentation](https://doc.traefik.io/traefik/providers/docker/#network). 12 | - The `backend` network is the network used to communicate between Docker containers. This is only referenced in the Traefik configuration. This can be renamed to your liking. In the configuration, this is referenced as an `external` Docker network, but can also be an `internal` Docker network ([documentation here](https://docs.docker.com/compose/how-tos/networking/#use-a-pre-existing-network)). 13 | 14 | ## Limitations 15 | 16 | - If there is another service using the `/api`, `/pictrs`, `/feeds`, `/nodeinfo`, `/.well-known`, `/inbox`, `/version`, and `/sitemap.xml` paths on `short.domain`, it will conflict with the Lemmy server. Similarly, if there is a service on `short.domain` listening for the `Accept: application/activity+json` HTTP header, it will also conflict with the Lemmy server. 17 | 18 | ## Traefik 19 | 20 | This is the solution that is currently being tested (by this author). Federation is not a problem whatsoever, and everything is working correctly. 21 | 22 | You can download the Docker Compose file by running: 23 | 24 | ```sh 25 | wget https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/assets/separate_subdomains/traefik/compose.yml 26 | ``` 27 | 28 | Then continue the [Docker instructions here](https://join-lemmy.org/docs/administration/install_docker.html), excluding the NGINX steps. 29 | 30 | ## NGINX 31 | 32 | This solution is derived from the Traefik configuration above. Please note, some configuration options may be unnecessary due to the fact that this was derived from a Traefik configuration. Feel free to open a [pull request](https://github.com/LemmyNet/lemmy-docs). 33 | 34 | You can download the `nginx_internal.conf` file by running: 35 | 36 | ```sh 37 | wget https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/assets/separate_subdomains/nginx/nginx_internal.conf 38 | ``` 39 | 40 | Ensure you edit `nginx_internal.conf` and replace `{{ nginx_internal_resolver }}` with `127.0.0.11` (use `10.89.0.1` for RedHat distributions). 41 | 42 | Then continue installing with your preferred installation method. For example, [these are the Docker instructions](https://join-lemmy.org/docs/administration/install_docker.html). Ensure you use the `nginx_internal.conf` file provided on this page. 43 | -------------------------------------------------------------------------------- /src/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Lemmy is a selfhosted, federated social link aggregation and discussion forum. It consists of many different communities which are focused on different topics. Users can post text, links or images and discuss it with others. Voting helps to bring the most interesting items to the top. There are strong moderation tools to keep out spam and trolls. All this is completely free and open, not controlled by any company. This means that there is no advertising, tracking, or secret algorithms. 4 | 5 | Federation is a form of decentralization. Instead of a single central service that everyone uses, there are multiple services that any number of people can use. 6 | 7 | A Lemmy website can operate alone. Just like a traditional website, people sign up on it, post messages, upload pictures and talk to each other. Unlike a traditional website, Lemmy instances can interoperate, letting their users communicate with each other; just like you can send an email from your Gmail account to someone from Outlook, Fastmail, Proton Mail, or any other email provider, as long as you know their email address, you can mention or message anyone on any website using their address. 8 | 9 | Lemmy uses a standardized, open protocol to implement federation which is called ActivityPub. Any software that likewise implements federation via ActivityPub can seamlessly communicate with Lemmy, just like Lemmy instances communicate with one another. 10 | 11 | The fediverse ("federated universe") is the name for all instances that can communicate with each other over ActivityPub and the World Wide Web. That includes all Lemmy servers, but also other implementations: 12 | 13 | - [Mastodon](https://joinmastodon.org/) (microblogging) 14 | - [PeerTube](https://joinpeertube.org/) (videos) 15 | - [Friendica](https://friendi.ca/) (multi-purpose) 16 | - and many more! 17 | 18 | In practical terms: Imagine if you could follow a Facebook group from your Reddit account and comment on its posts without leaving your account. If Facebook and Reddit were federated services that used the same protocol, that would be possible. With a Lemmy account, you can communicate with any other compatible instance, even if it is not running on Lemmy. All that is necessary is that the software support the same subset of the ActivityPub protocol. 19 | 20 | Unlike proprietary services, anyone has the complete freedom to run, examine, inspect, copy, modify, distribute, and reuse the Lemmy source code. Just like how users of Lemmy can choose their service provider, you as an individual are free to contribute features to Lemmy or publish a modified version of Lemmy that includes different features. These modified versions, also known as software forks, are required to also uphold the same freedoms as the original Lemmy project. Because Lemmy is libre software that respects your freedom, personalizations are not only allowed but encouraged. 21 | 22 | You can contribute to this documentation in the [git repository](https://github.com/LemmyNet/lemmy-docs). 23 | 24 | This page is adapted from [Mastodon documentation](https://docs.joinmastodon.org/) under CC BY-SA 4.0. 25 | -------------------------------------------------------------------------------- /src/users/03-votes-and-ranking.md: -------------------------------------------------------------------------------- 1 | # Votes and Ranking 2 | 3 | ## Posts 4 | 5 | Lemmy uses a voting system to sort post listings. On the left side of each post there are _up_ and _down_ arrows, which let you _upvote_ or _downvote_ it. You can upvote posts that you like so that more users will see them, or downvote posts so that they are less likely to be seen. Each post receives a score which is the number of upvotes minus the number of downvotes. 6 | 7 | ### Sorting Posts 8 | 9 | When browsing the front page or a community, you can choose between the following sort types for posts: 10 | 11 | - **Active** (default): Calculates a rank based on the score and time of the latest comment, with decay over time 12 | - **Hot**: Like active, but uses time when the post was published 13 | - **Scaled**: Like hot, but gives a boost to less active communities 14 | - **New**: Shows most recent posts first 15 | - **Old**: Shows oldest posts first 16 | - **Most Comments**: Shows posts with highest number of comments first 17 | - **New Comments**: Bumps posts to the top when they are created or receive a new reply, analogous to the sorting of traditional forums 18 | - **Top Day**: Highest scoring posts during the last 24 hours 19 | - **Top Week**: Highest scoring posts during the last 7 days 20 | - **Top Month**: Highest scoring posts during the last 30 days 21 | - **Top Year**: Highest scoring posts during the last 12 months 22 | - **Top All Time**: Highest scoring posts of all time 23 | 24 | ## Comments 25 | 26 | Comments are by default arranged in a hierarchy which shows at a glance who it is replying to. Top-level comments which reply directly to a post are on the very left, not indented at all. Comments that are responding to top-level comments are indented one level and each further level of indentation means that the comment is deeper in the conversation. With this layout, it is always easy to see the context for a given comment, by simply scrolling up to the next comment which is indented one level less. 27 | 28 | ### Sorting Comments 29 | 30 | Comments can be sorted in the following ways. These all keep the indentation intact, so only replies to the same parent are shuffled around. 31 | 32 | - **Hot** (default): Equivalent to the _Hot_ sort for posts 33 | - **Top**: Shows comments with highest score first 34 | - **New**: Shows most recent comments first 35 | - **Old**: Shows oldest comments first 36 | 37 | Additionally there is a sort option **Chat**. This eliminates the hierarchy, and puts all comments on the top level, with newest comments shown at the top. It is useful to see new replies at any point in the conversation, but makes it difficult to see the context. 38 | 39 | The ranking algorithm is described in detail [here](../contributors/07-ranking-algo.md). 40 | 41 | ## Vote Privacy 42 | 43 | Lemmy attempts to limit the visibility of votes to protect user privacy. But due to the way Lemmy works, votes cannot be completely private. Instance admins can see the names of everyone who voted on a given post or comment, and community moderators can see the names for the communities they moderate. This helps to fight against vote manipulation. Additionally, individual votes are federated over ActivityPub together with the corresponding username. This means that other federated platforms can freely choose how to display vote information, even going as far as publicly displaying individual votes. 44 | -------------------------------------------------------------------------------- /src/administration/install_docker.md: -------------------------------------------------------------------------------- 1 | # Install with Docker 2 | 3 | Make sure you have both docker and docker-compose(>=`2.0`) installed. On Ubuntu, just run `apt install docker-compose-v2 docker.io`. On Debian, you need to install Docker [using their official installation instructions and custom `apt` repo](https://docs.docker.com/engine/install/debian/). 4 | 5 | Create a folder for the lemmy files. the location doesnt matter. You can put this anywhere you want, for example under `/srv/`: 6 | 7 | ```bash 8 | mkdir lemmy 9 | cd lemmy 10 | ``` 11 | 12 | Then download default config files: 13 | 14 | ```bash 15 | wget https://raw.githubusercontent.com/LemmyNet/lemmy-docs/main/assets/docker-compose.yml 16 | wget https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/examples/config.hjson -O lemmy.hjson 17 | wget https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/templates/nginx_internal.conf 18 | wget https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/files/proxy_params 19 | ``` 20 | 21 | Edit `docker-compose.yml` and `lemmy.hjson` to replace all occurrences of `{{ domain }}` with your actual Lemmy domain, `{{ postgres_password }}` with a random password. 22 | 23 | Also edit `nginx_internal.conf` and replace `{{ nginx_internal_resolver }}` with `127.0.0.11` (use `10.89.0.1` for RedHat distributions). 24 | 25 | If you'd like further customization, have a look at the [config file](configuration.md) named `lemmy.hjson`, and adjust it accordingly. 26 | 27 | ## Database tweaks 28 | 29 | To optimize your database, add this file. 30 | 31 | You can input your system specs, using this tool: https://pgtune.leopard.in.ua/ 32 | 33 | `wget https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/examples/customPostgresql.conf` 34 | 35 | ## Folder permissions 36 | 37 | Set the correct permissions for pictrs folder: 38 | 39 | ```bash 40 | mkdir -p volumes/pictrs 41 | sudo chown -R 991:991 volumes/pictrs 42 | ``` 43 | 44 | Finally, run: 45 | 46 | `docker compose up -d` 47 | 48 | lemmy-ui is accessible on the server at `http://localhost:{{ lemmy_port }}` 49 | 50 | ## Reverse Proxy / Webserver 51 | 52 | Here's an optional nginx reverse proxy template, which you can place in `/etc/nginx/sites-enabled` 53 | 54 | Alternatively, you can use any other web server such as caddy as a simple reverse proxy. 55 | 56 | Be sure to edit the `{{ }}` to match your domain and port. 57 | 58 | If you're using this, you will need to set up Let's Encrypt. See those instructions below. 59 | 60 | `wget https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/templates/nginx.conf` 61 | 62 | If you've set up Let's Encrypt and your reverse proxy, you can go to `https://{{ domain }}` 63 | 64 | ## Let's Encrypt 65 | 66 | You should also setup TLS, for example with [Let's Encrypt](https://letsencrypt.org/). [Here's a guide for setting up letsencrypt on Ubuntu](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-22-04). 67 | 68 | For federation to work, it is important that you do not change any headers that form part of the signature. This includes the `Host` header - you may need to refer to the documentation for your proxy server to pass through the `Host` header unmodified. 69 | 70 | # Updating 71 | 72 | To update to the newest version, it is usually enough to change the version numbers in `docker-compose.yml`, eg `dessalines/lemmy:0.19.4` to `dessalines/lemmy:0.19.5`. If additional steps are required, these are explained in the respective release announcement. After changing the versions, run these commands: 73 | 74 | ```bash 75 | docker compose pull 76 | docker compose up -d 77 | ``` 78 | -------------------------------------------------------------------------------- /src/contributors/08-plugins.md: -------------------------------------------------------------------------------- 1 | # Lemmy Plugins 2 | 3 | A plugin system for Lemmy is currently in [experimental state](https://github.com/LemmyNet/lemmy/pull/4695). At this point the design is not final, so as plugin developer you can help us design a sensible API. You can post any experiences or problems with plugins in the pull request linked above. Only once there is some developer feedback will the plugin system be merged and released. 4 | 5 | To get started writing plugins, follow the instructions in [Local Development](https://join-lemmy.org/docs/contributors/02-local-development.html) to setup a local test instance from git. Checkout the `plugin-system` branch. See the [Extism documentation](https://github.com/extism/extism?tab=readme-ov-file#compile-webassembly-to-run-in-extism-hosts) for information how to write a plugin, and have a look at the [sample plugin in Golang](https://github.com/LemmyNet/lemmy/tree/ef76b48505661cea92d15cf46c40c9dc3496b746/plugins). To test your plugin, place the compiled `.wasm` file in the `plugins` folder and start Lemmy. 6 | 7 | Plugins are invoked on specific hooks. API hooks are defined based on the HTTP method and path, with the form `api_before_{method}_{path}`. You can find the name of specific plugin hooks by running Lemmy, invoking the desired API call and grepping the logs for `Calling plugin hook`. Then declare hook in your plugin code. 8 | 9 | ### API `before` hooks 10 | 11 | These are called before a given API endpoint is invoked, with the raw JSON data submitted by the user, and need to return data in the same format. At this point the data can be invalid, so the actions may still be rejected. These hooks are mainly useful to modify data before it is stored in the database. For example writing a slur filter, automatically marking posts as nsfw or permanently rewriting URLs. 12 | 13 | Examples: 14 | 15 | - `api_before_post` with [CreatePost](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/src/post.rs#L20) 16 | - `api_post_user_register` with [Register](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/src/person.rs#L39) 17 | - `api_post_community_delete` with [DeleteCommunity](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/src/community.rs#L174) 18 | 19 | ### API `after` hooks 20 | 21 | Called after an endpoint was successfully invoked and gets passed the API return data. It also needs to return the same back, which allows temporarily modifying responses or adding extra data to responses. These hooks can also be used to trigger notifications or other external actions. 22 | 23 | Examples: 24 | 25 | - `api_after_get_post_list` with [GetPostsResponse](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/src/post.rs#L93) 26 | - `api_after_post_comment_like` with [CommentResponse](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/src/comment.rs#L89) 27 | - `api_after_post_community_ban_user` with [BanFromCommunityResponse](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/src/community.rs#L112) 28 | 29 | ### Federation hooks 30 | 31 | The data structures for federation are completely different from those used in the API, which is why they have separate plugin hooks. Like with the API, there are `before` hooks which can be used to permanently change data before it is written to the database. There are also after hooks which can be used for notifications etc. 32 | 33 | At the moment only the following hook is available, more will be added later: 34 | 35 | - `federation_before_receive_post` with [PostInsertForm](https://github.com/LemmyNet/lemmy/blob/main/crates/db_schema/src/source/post.rs#L67) 36 | - `federation_after_receive_post` with [Post](https://github.com/LemmyNet/lemmy/blob/main/crates/db_schema/src/source/post.rs#L18) 37 | -------------------------------------------------------------------------------- /assets/separate_subdomains/nginx/nginx_internal.conf: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | # Docker internal DNS IP so we always get the newer containers without having to 9 | # restart/reload the docker container / nginx configuration 10 | resolver {{ nginx_internal_resolver }} valid=5s; 11 | 12 | # set the real_ip when from docker internal ranges. Ensuring our internal nginx 13 | # container can always see the correct ips in the logs 14 | set_real_ip_from 10.0.0.0/8; 15 | set_real_ip_from 172.16.0.0/12; 16 | set_real_ip_from 192.168.0.0/16; 17 | 18 | # We construct a string consistent of the "request method" and "http accept header" 19 | # and then apply soem ~simply regexp matches to that combination to decide on the 20 | # HTTP upstream we should proxy the request to. 21 | # 22 | # Example strings: 23 | # 24 | # "GET:application/activity+json" 25 | # "GET:text/html" 26 | # "POST:application/activity+json" 27 | # 28 | # You can see some basic match tests in this regex101 matching this configuration 29 | # https://regex101.com/r/vwMJNc/1 30 | # 31 | # Learn more about nginx maps here http://nginx.org/en/docs/http/ngx_http_map_module.html 32 | map "$request_method:$http_accept" $proxpass { 33 | # If no explicit matches exists below, send traffic to Alexandrite 34 | default "http://alexandrite:3000"; 35 | 36 | # GET/HEAD requests that accepts ActivityPub or Linked Data JSON should go to lemmy. 37 | # 38 | # These requests are used by Mastodon and other fediverse instances to look up profile information, 39 | # discover site information and so on. 40 | "~^(?:GET|HEAD):.*?application\/(?:activity|ld)\+json" "http://lemmy:8536"; 41 | 42 | # All non-GET/HEAD requests should go to lemmy 43 | # 44 | # Rather than calling out POST, PUT, DELETE, PATCH, CONNECT and all the verbs manually 45 | # we simply negate the GET|HEAD pattern from above and accept all possibly $http_accept values 46 | "~^(?!(GET|HEAD)).*:" "http://lemmy:8536"; 47 | } 48 | 49 | server { 50 | set $lemmy "lemmy:8536"; 51 | # this is the port inside docker, not the public one yet 52 | listen 8536; 53 | 54 | ## CHANGE `short.domain` HERE 55 | server_name short.domain; 56 | server_tokens off; 57 | 58 | # Upload limit, relevant for pictrs 59 | client_max_body_size 20M; 60 | 61 | # Send actual client IP upstream 62 | include proxy_params; 63 | 64 | # backend 65 | location ~ ^/(api|pictrs|feeds|nodeinfo|.well-known|version|sitemap.xml) { 66 | proxy_pass "http://$lemmy"; 67 | 68 | # Send actual client IP upstream 69 | include proxy_params; 70 | } 71 | } 72 | 73 | server { 74 | set $lemmy_ui "lemmy-ui:1234"; 75 | # this is the port inside docker, not the public one yet 76 | listen 1236; 77 | 78 | ## CHANGE `sub.londerdomain.com` HERE 79 | server_name sub.longerdomain.com; 80 | server_tokens off; 81 | 82 | # Upload limit, relevant for pictrs 83 | client_max_body_size 20M; 84 | 85 | # Send actual client IP upstream 86 | include proxy_params; 87 | 88 | # frontend general requests 89 | location / { 90 | proxy_pass $proxpass; 91 | rewrite ^(.+)/+$ $1 permanent; 92 | } 93 | 94 | # security.txt 95 | location = /.well-known/security.txt { 96 | proxy_pass "http://$lemmy_ui"; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/administration/first_steps.md: -------------------------------------------------------------------------------- 1 | # First Steps 2 | 3 | After you successfully installed Lemmy either [manually with Docker](install_docker.md) or [automatically with Ansible](install_ansible.md) here are some recommendations for a new administrator of a Lemmy server. 4 | 5 | ## Admin Settings 6 | 7 | The first thing to do is to go to your admin panel, which can be found by clicking on the cog at the top right next to the search icon. Here you can define a description for your site, so that people know if it is about one specific topic or if all subjects are welcome. You can also add an icon and a banner that define your server, it can for example be the logo of your organization. 8 | 9 | Take the time to browse through the entire page to discover the different options you have to customize your Lemmy instance, on the same page you can edit your [configuration file](configuration.md), where you can find information about your database, the email used by the server, the federation options or who is the main administrator. 10 | 11 | It is always good to define another administrator than yourself, in case it is necessary to take actions while you take your best nap. Take a look at the [moderation guide](../users/04-moderation.md) for more information on how to do this. 12 | 13 | ## Check that everything is working properly 14 | 15 | ### Email 16 | 17 | The easiest way to check that the email is set up correctly is to request a password renewal. You will need to set up an email in your settings if you have not already done so. 18 | 19 | After that just log out, go to the `Login` page, enter your email in the `Email or Username` box and press `forgot password`. If everything is set up correctly, you should receive an email to renew your password. You can ignore this email. 20 | 21 | ### Federation 22 | 23 | Federation is disabled by default, and needs to be enabled either through the online admin panel or directly through the `config.json` file. 24 | 25 | To test that your instance federation is working correctly execute `curl -H 'Accept: application/activity+json' https://your-instance.com/u/your-username`, it should return json data, and not an .html file. If that is unclear to you, it should look similar to the output of `curl -H 'Accept: application/activity+json' https://lemmy.ml/u/nutomic`. 26 | 27 | ## Inclusion on join-lemmy.org instance list 28 | 29 | To be included in the list of Lemmy instances on [join-lemmy.org](https://join-lemmy.org/instances) you must meet the following requirements: 30 | 31 | - [x] Federate with at least one instance from the list 32 | - [x] Have a site description and icon 33 | - [x] Make sure you require registration applications. This helps keep bots and trolls from using your server to spam the fediverse. 34 | - [x] Have at least 5 users who posted or commented at least once in the past month 35 | - [x] Be on the latest major version of Lemmy 36 | - [x] Be patient and wait the site to be updated, there's no fixed schedule for that 37 | 38 | Recommended instances are defined in code [here](https://github.com/LemmyNet/joinlemmy-site/blob/main/recommended-instances.json) 39 | and the code that powers the crawler is visible [here](https://github.com/LemmyNet/lemmy-stats-crawler). 40 | 41 | In the meantime you can always promote your server on other social networks like Mastodon using the hashtag `#Lemmy`. 42 | 43 | ## Keeping up to date 44 | 45 | You can subscribe to the Github RSS feeds to be informed of the latest releases: 46 | 47 | - [lemmy](https://github.com/LemmyNet/lemmy/releases.atom) 48 | - [lemmy-ui](https://github.com/LemmyNet/lemmy-ui/releases.atom) 49 | - [lemmy-js-client](https://github.com/LemmyNet/lemmy-js-client/releases.atom) 50 | - [joinlemmy-site](https://github.com/LemmyNet/joinlemmy-site/releases.atom) 51 | 52 | There is also a [Matrix chat](https://matrix.to/#/#lemmy-admin-support-topics:discuss.online) for instance administrators that you can join. You'll find some really friendly people there who will help you (or at least try to) if you run into any issue. 53 | -------------------------------------------------------------------------------- /src/administration/federation_getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting started with Federation 2 | 3 | Federation is enabled by default, but initially you won't see any content from other Lemmy instances. To get started you can browse [other Lemmy instances](https://join-lemmy.org/instances) for interesting communities, or browse the community list on [lemmyverse.net](https://lemmyverse.net/communities). Once you find an interesting community, simply paste the URL into the search field on your own Lemmy instance, for example `https://lemmy.ml/c/announcements`. To have new content in the community pushed to your instance, you need to click the "Follow" button in the sidebar. 4 | 5 | If you notice that community follows are still shown as "Subscription Pending" after a few hours, or your posts are not visible on other instances, you may have a configuration problem. Check out the [troubleshooting guide](troubleshooting.md), you can also ask for help in [!lemmy@lemmy.ml](https://lemmy.ml/c/lemmy) or the [admin chat on Matrix](https://matrix.to/#/#lemmy-support-general:discuss.online). 6 | 7 | ## Fetching Data 8 | 9 | If you see something interesting while browsing another Lemmy instance, you can easily import it in order to interact with the content. This works for users, communities, posts and comments. For posts and comments, you can get the correct URL from the colorful "Fedilink" icon. All of the following formats are valid to fetch data over federation: 10 | 11 | - `https://lemmy.ml/c/programming` (Community) 12 | - `https://lemmy.ml/u/nutomic` (User) 13 | - `https://lemmy.ml/post/123` (Post) 14 | - `https://lemmy.ml/comment/321` (Comment) 15 | - `!main@lemmy.ml` (Community) 16 | - `@nutomic@lemmy.ml` (User) 17 | 18 | When fetching a community, the most recent posts are also fetched automatically (but not comments or votes). When fetching a post, the community and author are also fetched automatically. And for comments, all parent comments, the post, their respective authors and the community are fetched. Sibling comments are not fetched automatically. If you want more comments from older posts, you have to search for each of them as described above. 19 | 20 | You can also fetch content from other Fediverse platforms such as [Piefed](https://join.piefed.social/) or [Mbin](https://joinmbin.org/). Other platforms may also work, but only if they use communities like Lemmy. 21 | 22 | ## Blocking or Allowing Instances 23 | 24 | Lemmy has three types of federation: 25 | 26 | - Allowlist: Explicitly list instances to connect to. 27 | - BlockList: Explicitly list instances to not connect to. Federation is open to all other instances. 28 | - Open: Federate with all potential instances. 29 | 30 | You can add allowed and blocked instances, by adding a list in your instance admin panel. IE to only federate with these instances, add: `enterprise.lemmy.ml,lemmy.ml` to the allowed instances section. 31 | 32 | Lemmy uses the ActivityPub protocol (a W3C standard) to enable federation between different servers (often called instances). This is very similar to the way email works. For example, if you use gmail.com, then you can not only send mails to other gmail.com users, but also to yahoo.com, yandex.ru and so on. Email uses the SMTP protocol to achieve this, so you can think of ActivityPub as "SMTP for social media". The many of different actions possible on social media (post, comment, like, share, etc) make that ActivityPub is much more complicated than SMTP. 33 | 34 | As with email, ActivityPub federation happens only between servers. So if you are registered on `enterprise.lemmy.ml`, you only connect to the API of `enterprise.lemmy.ml`, while the server takes care of sending and receiving data from other instances (eg `voyager.lemmy.ml`). The great advantage of this approach is that the average user doesn't have to do anything to use federation. In fact if you are using Lemmy, you are likely already using it. One way to confirm is by going to a community or user profile. If you are on `enterprise.lemmy.ml` and you see a user like `@nutomic@voyager.lemmy.ml`, or a community like `!main@ds9.lemmy.ml`, then those are federated, meaning they use a different instance from yours. 35 | -------------------------------------------------------------------------------- /src/users/08-history-of-lemmy.md: -------------------------------------------------------------------------------- 1 | # History of Lemmy 2 | 3 | The idea to make Lemmy was a combination of factors. 4 | 5 | Open source developers like myself have long watched the rise of the “Big Five”, the US tech giants that have managed to capture nearly all the world’s everyday communication into their hands. We’ve been asking ourselves why people have moved away from content-focused sites, and what we can do to subvert this trend, in a way that is easily accessible to a non-tech focused audience. 6 | 7 | The barriers to entry on the web are much lower than say in the physical world: all it takes is a computer and some coding knowhow… yet the predominant social media firms have been able to stave off competition for at least two reasons: their sites are easy to use, and they have huge numbers of users already (the “first mover” advantage). The latter is more important; if you’ve ever tried to get someone to use a different chat app, you’ll know what I mean. 8 | 9 | Now I loved early Reddit, not just for the way that it managed to put all the news for the communities and topics I wanted to see in a single place, but for the discussion trees behind every link posted. I still have many of these saved, and have gained so much more from the discussion behind the links, than I have from the links themselves. In my view, its the community-focused, tree-like discussions, as well as the ability to make, grow, and curate communities, that has made Reddit the 5th most popular site in the US, and where so many people around the world get their news. 10 | 11 | But that ship sailed years ago; the early innovative spirit of Reddit left with Aaron Swartz: its libertarian founders have allowed some of the most racist and sexist online communities to fester on Reddit for years, only occasionally removing them when community outcry reaches a fever pitch. Reddit closed its source code years ago, and the Reddit redesign has become a bloated anti-privacy mess. 12 | 13 | Its become absorbed into that silicon valley surveillance-capitalist machine that commodifies users to sell ads and paid flairs, and propagandizes pro-US interests above all. Software technology being one of the last monopoly exports the US has, it would be naive to think that one of the top 5 most popular social media sites, where so many people around the world get their news, would be anything other than a mouthpiece for the interests of those same US coastal tech firms. 14 | 15 | Despite the conservative talking point that big tech is dominated by “leftist propaganda”, it is liberal, and pro-US, not left (leftism referring to the broad category of anti-capitalism). Reddit has banned its share of leftist users and communities, and the Reddit admins via announcement posts repeatedly vilify the US’s primary foreign-policy enemies as having “bot campaigns”, and “manipulating Reddit”, yet the default Reddit communities (/r/news, /r/pics, etc), who share a small number of moderators, push a line consistent with US foreign-policy interests. The aptly named /r/copaganda subreddit has exposed the pro-police propaganda that always seems to hit Reddit’s front page in the wake of every tragedy involving US police killing the innocent (or showing police kissing puppies, even though US police kill ~ 30 dogs every day, which researchers have called a “noted statistical phenomenon”). 16 | 17 | We’ve also seen a rise in anti-China posts that have hit Reddit lately, and along with that comes anti-chinese racism, which Reddit tacitly encourages. That western countries are seeing a rise in attacks against Asian-Americans, just as some of the perpetrators of several hate-crimes against women were found to be Redditors active in mens-rights Reddit communities, is not lost on us, and we know where these tech companies really stand when it comes to violence and hate speech. Leftists know that our position on these platforms is tenuous at best; we’re currently tolerated, but that will not always be the case. 18 | 19 | The idea for making a Reddit alternative seemed pointless, until Mastodon (a federated twitter alternative), started becoming popular. Using Activitypub (a protocol / common language that social media services can use to speak to each other), we finally have a solution to the “first mover” advantage: now someone can build or run a small site, but still be connected to a wider universe of users. 20 | 21 | Nutomic and I originally made Lemmy to fill the role as a federated alternative to Reddit, but as it grows, it has the potential become a main source of news and discussion, existing outside of the US’s jurisdictional domain and control. 22 | -------------------------------------------------------------------------------- /assets/separate_subdomains/traefik/compose.yml: -------------------------------------------------------------------------------- 1 | x-logging: &a1 2 | driver: json-file 3 | options: 4 | max-size: 50m 5 | max-file: "4" 6 | 7 | services: 8 | lemmy: 9 | image: dessalines/lemmy:0.19.14 10 | hostname: lemmy 11 | restart: always 12 | logging: *a1 13 | environment: 14 | - RUST_LOG="warn" 15 | # You can switch to the environment variable below to ensure federation is working correctly 16 | # - RUST_LOG=lemmy_federate=trace 17 | volumes: 18 | # This volume mapping, and all others, should be configured to point to the correct location on your system 19 | - ./lemmy.hjson:/config/config.hjson:Z 20 | depends_on: 21 | - postgres 22 | - pictrs 23 | # These should be changed to match your Traefik setup. 24 | # Please refer to the "Placeholders" section in the documentation. 25 | networks: 26 | - proxy 27 | - backend 28 | labels: 29 | - traefik.enable=true 30 | - traefik.http.services.lemmy.loadbalancer.server.port=8536 31 | - traefik.http.services.lemmy.loadbalancer.server.scheme=http 32 | - traefik.http.routers.lemmy-https.entrypoints=websecure 33 | - traefik.http.routers.lemmy-https.service=lemmy 34 | ## CHANGE `short.domain` HERE 35 | - traefik.http.routers.lemmy-https.rule=Host(`short.domain`) && (PathPrefix(`/api`) || PathPrefix(`/pictrs`) || PathPrefix(`/feeds`) || PathPrefix(`/nodeinfo`) || PathPrefix(`/.well-known`) || PathPrefix(`/inbox`) || Path(`/version`) || Path(`/sitemap.xml`) || Method(`POST`) || HeaderRegexp(`Accept`,`^[Aa]pplication/.*`)) 36 | - traefik.http.routers.lemmy-https.tls=true 37 | ## CHANGE `myresolver` TO BE YOUR TLS CERT RESOLVER 38 | - traefik.http.routers.lemmy-https.tls.certresolver=myresolver 39 | # This is the Lemmy UI. 40 | lemmy-ui: 41 | # image: **UPDATE WITH THE FIXED LEMMY UI VERSION** 42 | environment: 43 | - LEMMY_UI_LEMMY_INTERNAL_HOST=lemmy:8536 44 | ## CHANGE `short.domain` HERE 45 | - LEMMY_UI_LEMMY_EXTERNAL_HOST=short.domain 46 | - LEMMY_UI_HTTPS=true 47 | volumes: 48 | - ./volumes/lemmy-ui/extra_themes:/app/extra_themes 49 | depends_on: 50 | - lemmy 51 | restart: always 52 | networks: 53 | - proxy 54 | - backend 55 | labels: 56 | - traefik.enable=true 57 | - traefik.http.services.lemmy-ui.loadbalancer.server.port=1234 58 | - traefik.http.services.lemmy-ui.loadbalancer.server.scheme=http 59 | - traefik.http.routers.lemmy-ui-https.entrypoints=websecure 60 | - traefik.http.routers.lemmy-ui-https.service=lemmy-ui 61 | ## CHANGE `sub.longerdomain.com` HERE 62 | - traefik.http.routers.lemmy-ui-https.rule=Host(`sub.longerdomain.com`) 63 | - traefik.http.routers.lemmy-ui-https.tls=true 64 | ## CHANGE `myresolver` TO BE YOUR TLS CERT RESOLVER 65 | - traefik.http.routers.lemmy-ui-https.tls.certresolver=myresolver 66 | logging: *a1 67 | pictrs: 68 | image: asonix/pictrs:0.5.19 69 | # This needs to match the pictrs url in lemmy.hjson 70 | hostname: pictrs 71 | # We can set options to pictrs like this, here we set max. image size and forced format for conversion 72 | # entrypoint: /sbin/tini -- /usr/local/bin/pict-rs -p /mnt -m 4 --image-format webp 73 | environment: 74 | - PICTRS_OPENTELEMETRY_URL=http://otel:4137 75 | ## SET YOUR POSTGRES PASSWORD HERE 76 | - PICTRS__SERVER__API_KEY={{ postgres_password }} 77 | - RUST_BACKTRACE=full 78 | - PICTRS__MEDIA__VIDEO__VIDEO_CODEC=vp9 79 | - PICTRS__MEDIA__ANIMATION__MAX_WIDTH=256 80 | - PICTRS__MEDIA__ANIMATION__MAX_HEIGHT=256 81 | - PICTRS__MEDIA__ANIMATION__MAX_FRAME_COUNT=400 82 | user: 991:991 83 | volumes: 84 | - ./volumes/pictrs:/mnt:Z 85 | restart: always 86 | networks: 87 | - backend 88 | logging: *a1 89 | postgres: 90 | image: pgautoupgrade/pgautoupgrade:18-alpine 91 | hostname: postgres 92 | environment: 93 | - POSTGRES_USER=lemmy 94 | ## SET YOUR POSTGRES PASSWORD HERE 95 | - POSTGRES_PASSWORD={{ postgres_password }} 96 | - POSTGRES_DB=lemmy 97 | # This should match the `shared_buffers` setting in customPostgresql.conf 98 | shm_size: 1g 99 | volumes: 100 | - ./volumes/postgres:/var/lib/postgresql/data:Z 101 | - ./customPostgresql.conf:/etc/postgresql.conf 102 | restart: always 103 | networks: 104 | - backend 105 | logging: *a1 106 | 107 | # Network configuration 108 | # Change this to match your setup 109 | networks: 110 | proxy: 111 | external: true 112 | backend: 113 | external: true 114 | -------------------------------------------------------------------------------- /src/contributors/03-docker-development.md: -------------------------------------------------------------------------------- 1 | # Docker Development 2 | 3 | ## Dependencies 4 | 5 | Debian-based distro: 6 | 7 | ```bash 8 | sudo apt install git docker-compose 9 | sudo systemctl start docker 10 | git clone https://github.com/LemmyNet/lemmy --recursive 11 | ``` 12 | 13 | Arch-based distro: 14 | 15 | ```bash 16 | sudo pacman -S git docker-compose 17 | sudo systemctl start docker 18 | ``` 19 | 20 | Get the code with submodules: 21 | 22 | ```bash 23 | git clone https://github.com/LemmyNet/lemmy --recursive 24 | ``` 25 | 26 | ## Building 27 | 28 | Use these commands to create a custom container based on your local branch and tagged accordingly. 29 | 30 | This is useful if you want to modify the source code of your instance to add some extra functionalities which are not available in the main release. 31 | 32 | ```bash 33 | sudo docker build . -f docker/Dockerfile --build-arg RUST_RELEASE_MODE=release -t "lemmy:${git rev-parse --abbrev-ref HEAD}" 34 | ``` 35 | 36 | #### Build Troubleshooting 37 | 38 | In case the build fails, the following might help resolve it 39 | 40 | ##### Translations missing 41 | 42 | If you see an error like this 43 | 44 | ``` 45 | Error: FileRead { file: "translations/email/en.json", source: Os { code: 2, kind: NotFound, message: "No such file or directory" } } 46 | ``` 47 | 48 | Try these commands 49 | 50 | ```bash 51 | git submodule init && git submodule update 52 | ``` 53 | 54 | Then try building again 55 | 56 | ### Running custom build on your server 57 | 58 | If you want a custom docker build to run on your instance via docker, you don't need to upload to a container repository, you can upload directly from your PC through ssh. 59 | 60 | The following commands will copy the file to your instance and then load it onto your server's container registry 61 | 62 | ```bash 63 | LEMMY_SRV=lemmy.example.com # Add the FQDN, IP or hostname of your lemmy server here 64 | # We store in /tmp to avoid putting it in our local branch and committing it by mistake 65 | sudo docker save -o /tmp/customlemmy.tar lemmy:${git rev-parse --abbrev-ref HEAD} 66 | # We change permissios to allow our normal user to read the file as root might not have ssh keys 67 | sudo chown ${whoami} /tmp/${git rev-parse --abbrev-ref HEAD} 68 | scp /tmp/customlemmy.tar ${LEMMY_SRV}: 69 | ssh ${LEMMY_SRV} 70 | # This command should be run while in your lemmy server as the user you uploaded 71 | sudo docker load -i ${HOME}/customlemmy.tar 72 | ``` 73 | 74 | After the container is in your registry, simply change the docker-compose to have your own tag in the `image` key 75 | 76 | ``` 77 | image: lemmy:your_branch_name 78 | ``` 79 | 80 | Finally, reinitiate the container 81 | 82 | ``` 83 | docker-compose up -d 84 | ``` 85 | 86 | You should now be running your custom docker container. 87 | 88 | ### Running 89 | 90 | ```bash 91 | cd docker 92 | ./docker_update.sh 93 | ``` 94 | 95 | Then open [localhost:1236](http://localhost:1236) in your browser. 96 | 97 | Building with Docker is relatively slow. To get faster builds you need to use [local development](02-local-development.md) instead. 98 | 99 | ## Federation Development 100 | 101 | The federation setup allows starting a few local Lemmy instances, to test federation functionality. You can start it with the following commands: 102 | 103 | ```bash 104 | cd docker/federation 105 | ./start-local-instances.bash 106 | ``` 107 | 108 | The federation test sets up 5 instances: 109 | 110 | | Instance | Admin username | Location | Notes | 111 | | ------------- | -------------- | --------------------------------------- | --------------------------------------- | 112 | | lemmy-alpha | lemmy_alpha | [127.0.0.1:8540](http://127.0.0.1:8540) | federated with all other instances | 113 | | lemmy-beta | lemmy_beta | [127.0.0.1:8550](http://127.0.0.1:8550) | federated with all other instances | 114 | | lemmy-gamma | lemmy_gamma | [127.0.0.1:8560](http://127.0.0.1:8560) | federated with all other instances | 115 | | lemmy-delta | lemmy_delta | [127.0.0.1:8570](http://127.0.0.1:8570) | only allows federation with lemmy-beta | 116 | | lemmy-epsilon | lemmy_epsilon | [127.0.0.1:8580](http://127.0.0.1:8580) | uses blocklist, has lemmy-alpha blocked | 117 | 118 | You can log into each using the instance name, and `lemmylemmy` as the password, IE (`lemmy_alpha`, `lemmylemmy`). 119 | 120 | To start federation between instances, visit one of them and search for a user, community or post, like this. Note that 121 | the Lemmy backend runs on a different port than the frontend, so you have to increment the port number from 122 | the URL bar by one. 123 | 124 | - `http://lemmy-gamma:8561/u/lemmy-gamma` 125 | - `http://lemmy-alpha:8541/c/main` 126 | - `http://lemmy-beta:8551/post/3` 127 | -------------------------------------------------------------------------------- /src/contributors/02-local-development.md: -------------------------------------------------------------------------------- 1 | # Local Development 2 | 3 | ### Install build requirements 4 | 5 | Install the latest Rust version using [rustup](https://www.rust-lang.org/tools/install). 6 | 7 | Debian-based distro: 8 | 9 | ```bash 10 | sudo apt install git cargo pkg-config libpq-dev curl 11 | ``` 12 | 13 | Arch-based distro: 14 | 15 | ```bash 16 | sudo pacman -S git cargo pkg-config postgresql-libs curl 17 | ``` 18 | 19 | Fedora-based distro: 20 | 21 | ```bash 22 | sudo dnf install git cargo pkg-config libpq-devel curl postgresql-server postgresql-contrib 23 | ``` 24 | 25 | macOS: 26 | 27 | Install [Homebrew](https://brew.sh/) if you don't already have it installed. Then install Node and pnpm. 28 | 29 | ```bash 30 | brew install node 31 | ``` 32 | 33 | ### Install pnpm 34 | 35 | The install instructions are [here](https://pnpm.io/installation), or you can run 36 | 37 | ```bash 38 | npm install -g pnpm 39 | ``` 40 | 41 | ### Setup PostgreSQL database 42 | 43 | Debian-based distro: 44 | 45 | ```bash 46 | sudo apt install postgresql 47 | sudo systemctl start postgresql 48 | ``` 49 | 50 | Arch-based distro: 51 | 52 | ```bash 53 | sudo pacman -S postgresql 54 | sudo systemctl start postgresql 55 | # If the systemctl command errors, then run the following 56 | sudo mkdir /var/lib/postgres/data 57 | sudo chown postgres:postgres /var/lib/postgres/data 58 | sudo -u postgres initdb -D /var/lib/postgres/data 59 | sudo systemctl start postgresql 60 | ``` 61 | 62 | macOS: 63 | 64 | ```bash 65 | brew install postgresql 66 | brew services start postgresql 67 | $(brew --prefix postgresql)/bin/createuser -s postgres 68 | ``` 69 | 70 | Either execute `scripts/db-init.sh`, or manually initialize the postgres database: 71 | 72 | ```bash 73 | psql -c "create user lemmy with password 'password' superuser;" -U postgres 74 | psql -c 'create database lemmy with owner lemmy;' -U postgres 75 | export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy 76 | ``` 77 | 78 | ### Code formatting tools 79 | 80 | #### Taplo 81 | 82 | See installation instructions [here](https://taplo.tamasfe.dev/), or run 83 | 84 | ```bash 85 | cargo install taplo-cli --locked 86 | ``` 87 | 88 | #### pg_format 89 | 90 | Debian-based distro: 91 | 92 | ```bash 93 | sudo apt install pgformatter 94 | ``` 95 | 96 | Arch-based distro: 97 | 98 | ```bash 99 | sudo pacman -S pgformatter 100 | ``` 101 | 102 | macOS: 103 | 104 | ```bash 105 | brew install pgformatter 106 | ``` 107 | 108 | ### Get the code 109 | 110 | Clone frontend and backend code to local folders. Be sure to include `--recursive` to initialize git submodules. 111 | 112 | ```bash 113 | git clone https://github.com/LemmyNet/lemmy.git --recursive 114 | git clone https://github.com/LemmyNet/lemmy-ui.git --recursive 115 | ``` 116 | 117 | ### Backend development 118 | 119 | Use `cargo check` to find compilation errors. To start the Lemmy backend, run `cargo run`. It will bind to `0.0.0.0:8536`. 120 | 121 | After making changes, you need to format the code with `cargo +nightly fmt --all` and run the linter with `./scripts/lint.sh`. 122 | 123 | If you'd like to speed up compilation times, you can install [mold](https://github.com/rui314/mold), and [sccache](https://github.com/mozilla/sccache), then add the following to `~/.cargo/config.toml`. Note that the parameter `-Zthreads=x` only works on nightly Rust. 124 | 125 | ```toml 126 | [build] 127 | rustc-wrapper = "/usr/bin/sccache" 128 | 129 | [target.x86_64-unknown-linux-gnu] 130 | linker = "clang" 131 | rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold", "-Z", "threads=16"] 132 | ``` 133 | 134 | To make changes to the database schema: 135 | 136 | 1. Add a new migration in the migrations folder. This is most easily done by running `diesel migration generate` then editing the added files. 137 | 1. Run `./scripts/update_schema_file.sh`. 138 | 139 | ### Frontend development 140 | 141 | Install dependencies by running `pnpm i`. Then run `pnpm dev` to launch lemmy-ui locally. It automatically connects to the Lemmy backend on `localhost:8536`. Open [localhost:1234](http://localhost:1234) in your browser. The project is rebuilt automatically when you change any files. 142 | 143 | Note that this setup doesn't support image uploads. If you want to test those, you need to use the 144 | [Docker development](03-docker-development.md). 145 | 146 | ### Tests 147 | 148 | Run Rust unit tests: 149 | 150 | ```bash 151 | ./scripts/test.sh 152 | ``` 153 | 154 | To run federation/API tests, first add the following lines to `/etc/hosts`: 155 | 156 | ``` 157 | 127.0.0.1 lemmy-alpha 158 | 127.0.0.1 lemmy-beta 159 | 127.0.0.1 lemmy-gamma 160 | 127.0.0.1 lemmy-delta 161 | 127.0.0.1 lemmy-epsilon 162 | ``` 163 | 164 | Then run the following script: 165 | 166 | ```bash 167 | cd api_tests 168 | ./run-federation-test.sh 169 | ``` 170 | -------------------------------------------------------------------------------- /src/users/05-censorship-resistance.md: -------------------------------------------------------------------------------- 1 | # Censorship resistance 2 | 3 | Today's social media landscape is extremely centralized. The vast majority of users are concentrated on only a handful of platforms like Facebook, Reddit or Twitter. All of these are maintained by large corporations that are subject to profit motive and United States law. In recent years these platforms have increasingly censored users and entire communities, often with questionable justifications. It is only natural that those who are affected by this search for alternatives. This document is intended to help with the evaluation. 4 | 5 | For this purpose we will consider as censorship anything that prevents a person from expressing their opinion, regardless of any moral considerations. All the options explained here also have legitimate uses, such as deleting spam. Nevertheless it is important for users to understand why their posts are getting removed and how to avoid it. 6 | 7 | The first and most common source of censorship in this sense is the admin of a given Lemmy instance. Due to the way federation works, an admin has complete control over their instance, and can arbitrarily delete content or ban users. The moderation log helps to provide transparency into such actions. 8 | 9 | The second source of censorship is through legal means. This often happens for copyright violation, but can also be used for other cases. What usually happens in this case is that the instance admin receives a takedown notice from the hosting provider or domain registrar. If the targeted content is not removed within a few days, the site gets taken down. The only way to avoid this is to choose the hosting company and country carefully, and avoid those which might consider the content as illegal. 10 | 11 | Another way to censor is through social pressure on admins. This can range from spamming reports for unwanted content, to public posts from influential community members _demanding_ to take certain content down. Such pressure can keep mounting for days or weeks, making it seem like everyone supports these demands. But in fact it is often nothing more than a vocal minority. It is the task of admins to gauge the true opinion of their community. Community members should also push back if a minority tries to impose its views on everyone else. 12 | 13 | All of this shows that it is relatively easy to censor a single Lemmy instance. Even a group of instances can be censored if they share the same admin team, hosting infrastructure or country. Here it is important that an admin can only censor content on their own instance, or communities which are hosted on his instance. Other instances will be unaffected. So if there is a problem with censorship, it can always be solved by using a different Lemmy instance, or creating a new one. 14 | 15 | But what if the goal was to censor the entire Lemmy network? This is inherently difficult because there is no single entity which has control over all instances. The closest thing to such an entity are the developers, because they can make changes to the code that all the instances run. For example, developers could decide to implement a hardcoded block for certain domains, so that they can't federate anymore. However, changes need to be released and then installed by instance admins. Those who are affected would have no reason to upgrade. And because the code is open source, they could publish a forked software version without these blocks. So the effect would be very limited, but it would split the project and result in loss of reputation for the developers. This is probably the reason why it has never happened on any Fediverse platform. 16 | 17 | Lastly it might be possible to abuse software vulnerabilities for network-wide censorship. Imagine a bug in Lemmy or in the underlying software stack which allows the attacker to delete arbitrary content. This could remain undetected for a while if used sparingly, but would certainly be discovered after some time. And experience has shown that such critical flaws are fixed very quickly in open source software. It is also highly unlikely that critical vulnerabilities be present in multiple different Fediverse platforms at the same time. 18 | 19 | In conclusion, the best way to avoid censorship on Lemmy is through the existence of many independent instances. These should have different admins, different hosting providers and be located in different countries. Additionally users should follow the development process to watch for changes that might create a centralized point of control for all instances. Based on this explanation it should be clear that censorship on Lemmy is difficult, and can always be circumvented. This is in contrast to centralized platforms like Facebook or Reddit. They are not open source and can't be self-hosted, so it is necessary to switch to an entirely different platform to avoid censorship. And due to lack of federation, such a switch means losing contact with users who decide to stay on the censored platform. 20 | -------------------------------------------------------------------------------- /src/administration/backup_and_restore.md: -------------------------------------------------------------------------------- 1 | # Backup and Restore 2 | 3 | ## Docker and Ansible 4 | 5 | When using docker or ansible, there should be a `volumes` folder, which contains both the database, and all the pictures. Copy this folder to the new instance to restore your data. 6 | 7 | ### Full Database backup 8 | 9 | To take a complete backup of the DB to a `.sql.gz` file, you can run: 10 | 11 | ```bash 12 | docker compose exec postgres pg_dumpall -c -U lemmy | gzip > lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz 13 | ``` 14 | 15 | _For compression, you can use either gzip and gunzip, or xz and unxz._ 16 | 17 | ### A Sample backup script 18 | 19 | ```bash 20 | #!/bin/sh 21 | # DB Backup 22 | ssh MY_USER@MY_IP "docker compose exec postgres pg_dumpall -c -U lemmy" | gzip > ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz 23 | 24 | # Volumes folder Backup 25 | rsync -avP -zz --rsync-path="sudo rsync" MY_USER@MY_IP:/LEMMY_LOCATION/volumes ~/BACKUP_LOCATION/FOLDERNAME 26 | ``` 27 | 28 | ### Restoring the DB 29 | 30 | To restore, run: 31 | 32 | ```bash 33 | docker compose up -d postgres 34 | 35 | # Restore from the .sql.gz backup 36 | gunzip < db_dump.sql | docker compose exec -T postgres psql -U lemmy 37 | 38 | # Note: You may need to change the permissions on the postgres directory, depending on your system. 39 | chown -R $USER volumes 40 | docker compose restart postgres 41 | 42 | # Continue with the startup 43 | docker compose up -d 44 | ``` 45 | 46 | If you've accidentally already started the lemmy service, you need to clear out your existing database: 47 | 48 | ```bash 49 | # Drop the existing DB 50 | docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" 51 | 52 | # This also might be necessary when doing a db import with a different password. 53 | docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "alter user lemmy with password 'bleh'" 54 | ``` 55 | 56 | Then run the restore commands above. 57 | 58 | ### Changing your domain name 59 | 60 | If you haven't federated yet, you can change your domain name in the DB. **Warning: do not do this after you've federated, or it will break federation.** 61 | 62 | Get into `psql` for your docker: 63 | 64 | `docker compose exec postgres psql -U lemmy` 65 | 66 | ```sql 67 | -- Instance 68 | update instance set domain = replace (domain, 'old_domain', 'new_domain'); 69 | 70 | -- Site 71 | update site set actor_id = replace (actor_id, 'old_domain', 'new_domain'); 72 | update site set inbox_url = replace (inbox_url, 'old_domain', 'new_domain'); 73 | update site set sidebar = replace (sidebar, 'old_domain', 'new_domain'); 74 | 75 | -- Post 76 | update post set ap_id = replace (ap_id, 'old_domain', 'new_domain'); 77 | update post set url = replace (url, 'old_domain', 'new_domain'); 78 | update post set body = replace (body, 'old_domain', 'new_domain'); 79 | update post set thumbnail_url = replace (thumbnail_url, 'old_domain', 'new_domain'); 80 | 81 | -- Comments 82 | update comment set ap_id = replace (ap_id, 'old_domain', 'new_domain'); 83 | update comment set content = replace (content, 'old_domain', 'new_domain'); 84 | 85 | -- Private messages 86 | update private_message set ap_id = replace (ap_id, 'old_domain', 'new_domain'); 87 | update private_message set content = replace (content, 'old_domain', 'new_domain'); 88 | 89 | -- User 90 | update person set actor_id = replace (actor_id, 'old_domain', 'new_domain'); 91 | update person set inbox_url = replace (inbox_url, 'old_domain', 'new_domain'); 92 | update person set shared_inbox_url = replace (shared_inbox_url, 'old_domain', 'new_domain'); 93 | update person set bio = replace (bio, 'old_domain', 'new_domain'); 94 | update person set avatar = replace (avatar, 'old_domain', 'new_domain'); 95 | update person set banner = replace (banner, 'old_domain', 'new_domain'); 96 | 97 | -- Community 98 | update community set actor_id = replace (actor_id, 'old_domain', 'new_domain'); 99 | update community set followers_url = replace (followers_url, 'old_domain', 'new_domain'); 100 | update community set inbox_url = replace (inbox_url, 'old_domain', 'new_domain'); 101 | update community set shared_inbox_url = replace (shared_inbox_url, 'old_domain', 'new_domain'); 102 | update community set moderators_url = replace (moderators_url, 'old_domain', 'new_domain'); 103 | update community set featured_url = replace (featured_url, 'old_domain', 'new_domain'); 104 | update community set description = replace (description, 'old_domain', 'new_domain'); 105 | update community set icon = replace (icon, 'old_domain', 'new_domain'); 106 | update community set banner = replace (banner, 'old_domain', 'new_domain'); 107 | 108 | --- Custom Emoji 109 | update custom_emoji set image_url = replace (image_url, 'old_domain', 'new_domain'); 110 | 111 | ``` 112 | 113 | ## More resources 114 | 115 | - https://stackoverflow.com/questions/24718706/backup-restore-a-dockerized-postgresql-database 116 | -------------------------------------------------------------------------------- /src/contributors/04-api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | Lemmy has `OpenAPI` and javascript client docs, for app and client developers. 4 | 5 | | Version | OpenAPI | lemmy-js-client | 6 | | ------------- | -------------------- | ---------------------------------------- | 7 | | release/v0.19 | Not available | [JS-client](/lemmy-js-client-docs/v0.19) | 8 | | main / dev | [OpenAPI](/api/main) | [JS-client](/lemmy-js-client-docs/main) | 9 | 10 | Instead of using the API directly you can use one of the existing [libraries](https://github.com/dbeley/awesome-lemmy#libraries). You can either interact with a local development instance via `http://localhost:8536`, or connect to a production instance. The following instances are available for testing purposes: 11 | 12 | - https://enterprise.lemmy.ml/ 13 | - https://ds9.lemmy.ml/ 14 | - https://voyager.lemmy.ml/ 15 | 16 | Other unofficial API documentation is available at: 17 | 18 | - [lemmy.readme.io](https://lemmy.readme.io/) 19 | - [mv-gh.github.io/lemmy_openapi_spec](https://mv-gh.github.io/lemmy_openapi_spec/) 20 | 21 | ### Curl Examples 22 | 23 | **GET example** 24 | 25 | The current API `{version}` is [here](https://github.com/LemmyNet/lemmy-js-client/blob/main/src/other_types.ts#L1). 26 | 27 | ``` 28 | curl "https://lemmy.ml/api/{version}/community/list?sort=Hot"` 29 | ``` 30 | 31 | **POST example** 32 | 33 | ``` 34 | curl -i -H \ 35 | "Content-Type: application/json" \ 36 | -X POST \ 37 | -d '{ 38 | "comment_id": 374, 39 | "score": 1, 40 | "auth": eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiaXNzIjoidGVzdC5sZW1teS5tbCJ9.P77RX_kpz1a_geY5eCp29sl_5mAm-k27Cwnk8JcIZJk 41 | }' \ 42 | https://lemmy.ml/api/{version}/comment/like 43 | ``` 44 | 45 | ### RSS/Atom feeds 46 | 47 | _These links can added by clicking on RSS icons in lemmy-ui._ 48 | 49 | | Type | url | 50 | | --------------- | -------------------------------------- | 51 | | All | `/feeds/all.xml?sort=Hot` | 52 | | Local | `/feeds/local.xml?sort=Hot` | 53 | | Community | `/feeds/c/community-name.xml?sort=Hot` | 54 | | User | `/feeds/u/user-name.xml?sort=Hot` | 55 | | Your front page | `/feeds/front.xml/{jwt_token}` | 56 | | Your inbox | `/feeds/inbox.xml/{jwt_token}` | 57 | | Your modlog | `/feeds/modlog.xml/{jwt_token}` | 58 | 59 | ### Images 60 | 61 | Lemmy forwards image requests to a locally running Pictrs. 62 | 63 | `GET /pictrs/image/{filename}?format={webp, jpg, ...}&thumbnail={96}` 64 | 65 | _Format and thumbnail are optional._ 66 | 67 | #### Create (request) 68 | 69 | Uploaded content must be valid multipart/form-data with an image array located within the images[] key. 70 | 71 | `POST /pictrs/image` 72 | 73 | #### Create (response) 74 | 75 | ``` 76 | { 77 | "files": [ 78 | { 79 | "delete_token": "{token}", 80 | "file": "{file}.jpg" 81 | } 82 | ], 83 | "msg": "ok" 84 | } 85 | ``` 86 | 87 | #### Delete 88 | 89 | `GET /pictrs/image/delete/{delete_token}/{file}` 90 | 91 | ### Rust API 92 | 93 | If you want to develop a Rust application which interacts with Lemmy, you can directly pull in the relevant API structs. This uses the exact API structs used in Lemmy, but with most heavyweight dependencies disabled. API paths or HTTP client are not included, so you need to handle those aspects manually. See the [readme](https://github.com/LemmyNet/lemmy/blob/main/crates/api_common/README.md) for details. 94 | 95 | ### Creating a Custom Frontend 96 | 97 | The Lemmy backend and frontend are completely separate projects. This creates a lot of potential for alternative frontends which can change much of the design and user experience of Lemmy. For example, it is possible to create a frontend in the style of a traditional forum like [phpBB](https://www.phpbb.com/), a question-and-answer site like [Stack Overflow](https://stackoverflow.com/), a blogging platform or an image gallery. This way you don't have to write any SQL queries, federation logic, API code and so on, but can use the proven implementation from Lemmy. It is also possible to run multiple frontends for a single Lemmy instance. 98 | 99 | #### Development 100 | 101 | The easiest way to get started is by forking one of the [existing frontends](https://join-lemmy.org/apps). But you can also create a new frontend from scratch. In any case the principle is the same: bind to a port to serve user requests, and connect to the API of a Lemmy instance as described above to fetch data. 102 | 103 | #### Translations 104 | 105 | You can add the [lemmy-translations](https://github.com/LemmyNet/lemmy-translations) repository to your project as a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules). That way you can take advantage of same translations used in the official frontend, and you will also receive new translations contributed via Weblate. 106 | 107 | #### Rate limiting 108 | 109 | Lemmy does rate limiting for many actions based on the client IP. But if you make any API calls on the server side (e.g. in the case of server-side rendering, or javascript pre-rendering), Lemmy will take the IP of the Docker container. Meaning that all requests come from the same IP, and get rate limited much earlier. To avoid this problem, you need to pass the actual client IP via `Forwarded` or `X-Forwarded-For` HTTP header. 110 | -------------------------------------------------------------------------------- /src/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | - We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. 4 | - Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all. 5 | - Please be kind and courteous. There’s no need to be mean or rude. 6 | - Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. 7 | - Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. 8 | - We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups. 9 | - Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the Lemmy moderation team immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back. 10 | - Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. 11 | - [Entitlement](https://tylercipriani.com/blog/2023/01/15/entitlement-burnout-and-toxicity/), ie demanding or insulting behavior towards someone for not doing what you want them to do, or not doing it fast enough, will also not be tolerated. 12 | 13 | [**Message the Moderation Team on Mastodon**](https://mastodon.social/@LemmyDev) 14 | 15 | ## Moderation 16 | 17 | These are the policies for upholding our community’s standards of conduct. If you feel that a thread needs moderation, please contact the Lemmy moderation team . 18 | 19 | 1. Remarks that violate the Lemmy standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) 20 | 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. 21 | 3. Moderators will first respond to such remarks with a warning, at the same time the offending content will likely be removed whenever possible. 22 | 4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off. 23 | 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. 24 | 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. 25 | 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, in private. Complaints about bans in-channel are not allowed. 26 | 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. 27 | 28 | In the Lemmy community we strive to go the extra step to look out for each other. Don’t just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they’re off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. 29 | 30 | And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make others comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. 31 | 32 | The enforcement policies listed above apply to all official Lemmy venues; including git repositories under [github.com/LemmyNet](https://github.com/LemmyNet) and [git.join-lemmy.org/LemmyNet](https://git.join-lemmy.org/LemmyNet/), the [Matrix chat](https://matrix.to/#/#lemmy-space:matrix.org); [lemmy.ml](https://lemmy.ml) and other instances under that domain. For other projects adopting the Lemmy Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. 33 | 34 | Adapted from the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct), which is based on the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/). 35 | -------------------------------------------------------------------------------- /src/users/04-moderation.md: -------------------------------------------------------------------------------- 1 | # Moderation 2 | 3 | The internet is full of bots, trolls and other malicious actors. Sooner or later they will post unwanted content to any website that is open to the public. It is the task of administrators and moderators to remove such unwanted content. Lemmy provides many tools for this, from removing individual posts and issuing temporary bans, to removing all content submitted by an offending user. 4 | 5 | Moderation in Lemmy is divided between administrators (_admins_) and moderators (_mods_). Admins are responsible for the entire instance, and can take action on any content. They are also the only ones who can completely ban users. In contrast, moderators are only responsible for a single community. Whereas admins can ban a user from the entire instance, mods can only ban them from their community. 6 | 7 | The most important thing that normal users can do if they notice a rule-breaking post is to use the report function. If you notice such a post, click the flag icon to notify mods and admins. This way they can take action quickly and remove the offending content. To find out about removals and other mod actions, you can use the mod log which is linked at the bottom of the page. In some cases there may be content that you personally dislike, but which doesn't violate any rules. For this, there is a block function which hides all posts from a given user or community. 8 | 9 | ## Rules 10 | 11 | Each instance has a set of rules to let users know which content is allowed or not. These rules can be found in the sidebar and apply to all local communities on that instance. Some communities may have their own rules in their respective sidebars, which apply in addition to the instance rules. 12 | 13 | Because Lemmy is decentralized, there is no single moderation team for the platform, nor any platform-wide rules. Instead each instance is responsible to create and enforce its own moderation policy. This means that two Lemmy instances can have rules that completely disagree or even contradict. This can lead to problems if they interact with each other, because by default federation is open to any instance that speaks the same protocol. To handle such cases, administrators can choose to block federation with specific instances. To be even safer, they can also choose to be federated only with instances that are allowed explicitly. 14 | 15 | ## How to Moderate 16 | 17 | To get moderator powers, you either need to create a new community, or be appointed by an existing moderator. Similarly, to become an admin, you need to create a new instance, or be appointed by an existing instance admin. Community moderation can be done over federation, you don't need to be registered on the same instance where the community is hosted. To be an instance administrator, you need an account on that specific instance. Admins and moderators are organized in a hierarchy, where the user who is listed first has the power to remove admins or mods who are listed later. 18 | 19 | All moderation actions are taken in the context menu of posts or comments. Click the three dot button to expand available mod actions, as shown in the screenshot below. All actions can be reverted in the same way. 20 | 21 | ![moderation_01.png](moderation_01.png) 22 | ![moderation_02.png](moderation_02.png) 23 | 24 | | Action | Result | Permission level | 25 | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | 26 | | Lock | Prevents making new comments under the post | Moderator | 27 | | Sticky (Community) | Pin the publication to the top of the community listing | Moderator | 28 | | Sticky (Local) | Pin the publication to the top of the front page | Admin | 29 | | Remove | Delete the post | Moderator | 30 | | Ban from community | Ban the user from interacting with the community, but they can still use the rest of the site. There is also an option to remove all existing posts. | Moderator | 31 | | Appoint as mod | Gives the user moderator status | Moderator | 32 | | Ban from site | Completely bans the account, so it cannot log in or interact at all. There is also an option to remove all existing posts. | Admin | 33 | | Purge user | Completely delete the user, including all posts and uploaded media. Use with caution. | Admin | 34 | | Purge post/comment | Completely delete the post, including attached media. | Admin | 35 | | Appoint as admin | Gives the user administrator status | Admin | 36 | -------------------------------------------------------------------------------- /src/administration/theming.md: -------------------------------------------------------------------------------- 1 | # Theming Guide 2 | 3 | ## Bootstrap 4 | 5 | Lemmy uses [Bootstrap v5](https://getbootstrap.com/), and very few custom css classes, so any bootstrap v5 compatible theme should work fine. Use a tool like [bootstrap.build](https://bootstrap.build/) to create a bootstrap v5 theme. Export the `bootstrap.min.css` once you're done, and save the `_variables.scss` too. 6 | 7 | ## Custom Theme Directory 8 | 9 | If you installed Lemmy with Docker, save your theme file to `./volumes/lemmy-ui/extra_themes`. For native installation (without Docker), themes are loaded by lemmy-ui from ./extra_themes folder. A different path can be specified with LEMMY_UI_EXTRA_THEMES_FOLDER environment variable. 10 | 11 | After a theme is added, users can select it under `/settings`. Admins can set a theme as site default under `/admin`. 12 | 13 | ## Default Theme Locations 14 | 15 | Default Lemmy themes are located in `/lemmy-ui/src/assets/css/themes`. Atom themes used for styling `` are in `/lemmy-ui/src/assets/css/code-themes`. Custom css classes and changes to the default Bootstrap styles are in `/lemmy-ui/src/assets/css/main.css`. 16 | 17 | ## Making CSS Themes with Sass and Bootstrap 18 | 19 | Some tips if making a theme based off the default Lemmy themes. 20 | 21 | Every theme has these files: 22 | 23 | - an output `theme.css` 24 | - an output `theme.css.map` 25 | - `theme.scss` 26 | - `_variables.theme.scss` 27 | 28 | All `_variables.theme.scss` files will inherit variables from `_variables.scss`. 29 | All `theme.scss` will import bootstrap variables from `"../../../../node_modules/bootstrap/scss/bootstrap";` and its `_variables.theme.scss` file. It may import additional variables from another theme if it is built off it. For example, `litely-compact.scss` imports from `_variables.litely.scss`. 30 | 31 | ### Using SCSS Files 32 | 33 | If you are new to Sass, keep in mind that `theme.scss` files are for css and Sass flavored css. `_variables.theme.scss` are for variables. 34 | 35 | #### Export Your CSS File 36 | 37 | To export your custom `.scss` and `_variables.theme.scss` files to a `.css` file open the command line in the same directory as your files and run: 38 | `sass theme.scss theme.css` which will generate the `.css` and `.css.map` files. 39 | 40 | ### Bootstrap Notes 41 | 42 | If you are new to Bootstrap, be aware that variables starting with a dollar sign like `$variable` are Bootstrap variables and when output to css will look like `--bs-variable`. You can also define custom root variables in your `_variables.theme.scss` files like `:root {--custom-variable: value;};` and you can refer to this again in your `theme.scss` file. 43 | 44 | #### Bootstrap Variables on Lemmy 45 | 46 | The Darkly and Litely themes use default Bootstrap variables to define the grayscales and colours. Inspecting the Bootstrap documentation for how colours are applied to elements is recommended, along with inspecting the CSS and output files. 47 | 48 | ##### Light and Dark Modes 49 | 50 | Even though `darkly.css` is a dark theme, it has in-built light and dark modes using media queries. The Bootstrap variables `$enable-dark-mode` and `$enable-light-mode` can be used to toggle this behaviour on or off. 51 | 52 | ##### Overwriting Variables 53 | 54 | Most Lemmy theming is done with Bootstrap's default variables. Some variables are defined with `!important` which means if you have defined them in your custom theme that unless it also has `!important` it will be overwritten. To check, do a search in one of the default theme files or use the Developer Tools in your browser. 55 | 56 | To quickly test your theme if you do not own a Lemmy instance, you can use a browser add on to load your custom CSS file. 57 | 58 | ##### External Stylesheets 59 | 60 | As users cannot currently upload their own themes in Settings (only Admin can do that), custom themes loaded with an external style sheet will need to take into account that users will have a pre-selected theme in Settings that may have conflicting styles with the custom theme. If a theme is developed from an existing theme, having the default theme selected in Settings can minimize style conflicts. 61 | 62 | ## How CSS Themes are Added to Settings 63 | 64 | In short, given the css theme is the correct file format and in the correct theme directory, it will be appended to the bottom of the theme list in Settings. The name is the filename minus the file extension. Details are below. 65 | 66 | ### CSS Format Check 67 | 68 | The Typescript file `theme-handler.ts` in `/lemmy-ui/src/server/handlers/` will check for existing `css` files from the custom theme folder (`./volumes/lemmy-ui/extra_themes` or `./extra_themes`). Non `css` files will trigger an error. 69 | 70 | ### Building the Theme List 71 | 72 | If a custom css theme is found, the handler will call `themes-list-handler.ts` which will load `build-themes-list.ts` from `/lemmy-ui/src/server/utils/`. The file `build-themes-list.ts` will search the directories for files ending in `.css` and build a list. 73 | 74 | Custom themes are appended to the bottom of the theme list. 75 | 76 | ### Theme Names 77 | 78 | `build-themes-list.ts` will remove the file extension `.css` from the theme filename to display in Settings. For example, `darkly-compact.css` will appear as `darkly-compact`. 79 | 80 | ## Build and Format Theme Files Quickly 81 | 82 | Some tips to save time which would be useful for theme developers or if you are submitting pull requests to [lemmy-ui (Github)](https://github.com/LemmyNet/lemmy-ui). 83 | 84 | After changing variables or Sass files: 85 | 86 | - run `pnpm themes:build` to automatically rebuild all Lemmy css theme files using Sass 87 | - run `npx prettier -w /lemmy-ui/src/assets/css/themes` to reformat the code in the themes directory with prettier 88 | -------------------------------------------------------------------------------- /src/administration/prometheus.md: -------------------------------------------------------------------------------- 1 | # Prometheus Metrics 2 | 3 | Lemmy supports the export of metrics in the [Prometheus](https://prometheus.io/) 4 | format. This document outlines how to enable the feature and begin collecting 5 | metrics. 6 | 7 | ## Configuration 8 | 9 | Configuration of the Prometheus endpoint is contained under the optional 10 | `prometheus` object in the `lemmy.hjson` file. In this configuration block, the 11 | `bind` address and `port` of the Prometheus metrics server can be configured. 12 | 13 | By default, it will serve on `127.0.0.1:10002`. If running inside a container, 14 | it should instead bind on all addresses as below. 15 | 16 | ``` 17 | prometheus: { 18 | bind: "0.0.0.0" 19 | port: 10002 20 | } 21 | ``` 22 | 23 | ## Scrape 24 | 25 | After Lemmy has been deployed, test that it is serving properly 26 | (substitute the correct hostname if not running locally). 27 | 28 | ```shell 29 | curl localhost:10002/metrics 30 | ``` 31 | 32 | ### Prometheus Configuration 33 | 34 | Below is a minimal configuration on the Prometheus server's side that will 35 | scrape the metrics from Lemmy. 36 | 37 | ```yaml 38 | global: 39 | scrape_configs: 40 | - job_name: lemmy 41 | scrape_interval: 1m 42 | static_configs: 43 | - targets: 44 | - localhost:10002 45 | ``` 46 | 47 | Then run a Prometheus server to scrape the metrics. 48 | 49 | ```shell 50 | docker run -p 9090:9090 -v prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus 51 | ``` 52 | 53 | If Lemmy was deployed using the `docker-compose.yml`, then use the following 54 | configurations instead. 55 | 56 | ```yaml 57 | global: 58 | scrape_configs: 59 | - job_name: lemmy 60 | scrape_interval: 1m 61 | static_configs: 62 | - targets: 63 | - lemmy:10002 64 | ``` 65 | 66 | Run the Prometheus server inside the same docker network that Lemmy is running 67 | in. This can be discovered by running `docker inspect` on the lemmy container. 68 | 69 | ```shell 70 | docker run -p 9090:9090 -v prometheus.yml:/etc/prometheus/prometheus.yml --network $LEMMY_NETWORK prom/prometheus 71 | ``` 72 | 73 | ## Example 74 | 75 | Below is an example of what is returned from the `/metrics` endpoint. Each 76 | combination of `endpoint`, `method`, and `status` will produce a histogram 77 | (`lemmy_api_http_requests_duration_seconds_*`), so for brevity the output was 78 | reduced to a single endpoint. 79 | 80 | For the HTTP metrics, this is saying that `/api/v3/post/list` received 12 `GET` 81 | requests that returned a `200` HTTP code. Cumulatively, these requests took 82 | 0.383 seconds. 5 requests completed between 0.01 and 0.025 seconds. 5 more 83 | completed between 0.025 and 0.05 seconds. And the remaining 2 requests completed 84 | between 0.05 and 0.1 seconds. 85 | 86 | ``` 87 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.005"} 0 88 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.01"} 0 89 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.025"} 5 90 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.05"} 10 91 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.1"} 12 92 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.25"} 12 93 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.5"} 12 94 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="1"} 12 95 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="2.5"} 12 96 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="5"} 12 97 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="10"} 12 98 | lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="+Inf"} 12 99 | lemmy_api_http_requests_duration_seconds_sum{endpoint="/api/v3/post/list",method="GET",status="200"} 0.3834808429999999 100 | lemmy_api_http_requests_duration_seconds_count{endpoint="/api/v3/post/list",method="GET",status="200"} 12 101 | 102 | lemmy_api_http_requests_total{endpoint="/api/v3/post/list",method="GET",status="200"} 12 103 | 104 | # HELP lemmy_db_pool_available_connections Number of available connections in the pool 105 | # TYPE lemmy_db_pool_available_connections gauge 106 | lemmy_db_pool_available_connections 2 107 | # HELP lemmy_db_pool_connections Current number of connections in the pool 108 | # TYPE lemmy_db_pool_connections gauge 109 | lemmy_db_pool_connections 2 110 | # HELP lemmy_db_pool_max_connections Maximum number of connections in the pool 111 | # TYPE lemmy_db_pool_max_connections gauge 112 | lemmy_db_pool_max_connections 5 113 | 114 | # HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. 115 | # TYPE process_cpu_seconds_total counter 116 | process_cpu_seconds_total 14 117 | # HELP process_max_fds Maximum number of open file descriptors. 118 | # TYPE process_max_fds gauge 119 | process_max_fds 1073741816 120 | # HELP process_open_fds Number of open file descriptors. 121 | # TYPE process_open_fds gauge 122 | process_open_fds 91 123 | # HELP process_resident_memory_bytes Resident memory size in bytes. 124 | # TYPE process_resident_memory_bytes gauge 125 | process_resident_memory_bytes 75603968 126 | # HELP process_start_time_seconds Start time of the process since unix epoch in seconds. 127 | # TYPE process_start_time_seconds gauge 128 | process_start_time_seconds 1688487611 129 | # HELP process_threads Number of OS threads in the process. 130 | # TYPE process_threads gauge 131 | process_threads 37 132 | # HELP process_virtual_memory_bytes Virtual memory size in bytes. 133 | # TYPE process_virtual_memory_bytes gauge 134 | process_virtual_memory_bytes 206000128 135 | ``` 136 | -------------------------------------------------------------------------------- /src/users/01-getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Choosing an Instance 4 | 5 | If you are used to sites like Reddit, then Lemmy works in a fundamentally different way. Instead of a single website like reddit.com, there are many different websites (called _instances_). These are operated by different people, have different topics and rules. Nevertheless, posts created in one instance can directly be seen by users who are registered on another. Its basically like email, but for social media. 6 | 7 | This means before using Lemmy and registering an account, you need to pick an instance. For this you can browse the [instance list](https://join-lemmy.org/instances) and look for one that matches your topics of interest. You can also see if the rules match your expectations, and how many users there are. It is better to avoid very big or very small instances. But don't worry too much about this choice, you can always create another account on a different instance later. 8 | 9 | [instance list screenshot] 10 | 11 | ## Registration 12 | 13 | Once you choose an instance, it's time to create your account. To do this, click _sign up_ in the top right of the page, or click the top right button on mobile to open a menu with _sign up_ link. 14 | 15 | [registration page screenshot] 16 | 17 | On the signup page you need to enter a few things: 18 | 19 | - **Username**: How do you want to be called? This name can not be changed and is unique within an instance. Later you can also set a _displayname_ which can be freely changed. If your desired username is taken, consider choosing a different instance where it is still available. 20 | - **Email**: Your email address. This is used for password resets and notifications (if enabled). Providing an email address is usually optional, but admins may choose to make it mandatory. In this case you will have to wait for a confirmation mail and click the link after completing this form. 21 | - **Password**: The password for logging in to your account. Make sure to choose a long and unique password which isn't used on any other website. 22 | - **Verify password**: Repeat the same password from above to ensure that it was entered correctly. 23 | 24 | There are also a few optional fields, which you may need to fill in depending on the instance configuration: 25 | 26 | - **Question/Answer**: Instance admins can set an arbitrary question which needs to be answered in order to create an account. This is often used to prevent spam bots from signing up. After submitting the form, you will need to wait for some time until the answer is approved manually before you can login. 27 | - **Code**: A captcha which is easy to solve for humans but hard for bots. Enter the letters and numbers that you see in the text box, ignoring uppercase or lowercase. Click the refresh button if you are unable to read a character. The _play_ button plays an audio version of the captcha. 28 | - **Show NSFW content**: Here you can choose if content that is "not safe for work" (or adult-only) should be shown. 29 | 30 | When you are done, press the _sign up_ button. 31 | 32 | It depends on the instance configuration when you can login and start using the account. In case the email is mandatory, you need to wait for the confirmation email and click the link first. In case "Question/Answer" is present, you need to wait for an admin to manually review and approve your registration. If you have problems with the registration, try to get in contact with the admin for support. You can also choose a different instance to sign up if your primary choice does not work. 33 | 34 | ## Following Communities 35 | 36 | After logging in to your new account, its time to follow communities that you are interested in. For this you can click on the _communities_ link at the top of the page (on mobile, you need to click the menu icon on the top right first). You will see a list of communities which can be filtered by subscribed, local or all. Local communities are those which are hosted on the same site where you are signed in, while _all_ also contains federated communities from other instances. In any case you can directly subscribe to communities with the right-hand subscribe link. Or click on the community name to browse the community first, see what its posted and what the rules are before subscribing. 37 | 38 | Another way to find communities to subscribe to is by going to the front page and browsing the posts. If there is something that interests you, click on the post title to see more details and comments. Here you can subscribe to the community in the right-hand sidebar, or by clicking the "sidebar" button on mobile. 39 | 40 | These previous ways will only show communities that are already known to the instance. Especially if you joined a small or inactive Lemmy instance, there will be few communities to discover. You can find more communities by browsing different Lemmy instances, or using the [Lemmy Explorer](https://lemmyverse.net/communities). When you found a community that you want to follow, enter its URL (e.g. `https://feddit.org/c/main`) or the identifier (e.g. `!main@feddit.org`) into the search field of your own Lemmy instance. Lemmy will then fetch the community from its original instance, and allow you to interact with it. The same method also works to fetch users, posts or comments from other instances. 41 | 42 | ## Setting up Your Profile 43 | 44 | Before you start posting, its a good idea to provide some details about yourself. Open the top-right menu and go to "settings". Here the following settings are available for your public profile: 45 | 46 | - **Displayname**: An alternative username which can be changed at any time 47 | - **Bio**: Long description of yourself, can be formatted with Markdown 48 | - **Matrix User**: Your username on the decentralized [Matrix chat](https://matrix.org/) 49 | - **Avatar**: Profile picture that is shown next to all your posts 50 | - **Banner**: A header image for your profile page 51 | 52 | On this page you can also change the email and password. Additionally there are many other settings available, which allow customizing of your browsing experience: 53 | 54 | - **Blocks** (tab at top of the page): Here you can block users and communities, so that their posts will be hidden. 55 | - **Interface language**: Which language the user interface should use. 56 | - **Languages**: Select the languages that you speak to see only content in these languages. This is a new feature and many posts don't specify a language yet, so be sure to select "Undetermined" to see them. 57 | - **Theme**: You can choose between different color themes for the user interface. Instance admins can add more themes. 58 | - **Type**: Which timeline you want to see by default on the front page; only posts from communities that you subscribe to, posts in local communities, or all posts including federated. 59 | - **Sort type**: How posts and comments should be sorted by default. See [Votes and Ranking](03-votes-and-ranking.md) for details. 60 | - **Show NSFW content**: Whether or not you want to see content that is "not safe for work" (or adult-only). 61 | - **Show Scores**: Whether the number of upvotes and downvotes should be visible. 62 | - **Show Avatars**: Whether profile pictures of other users should be shown. 63 | - **Bot Account**: Enable this if you are using a script or program to create posts automatically 64 | - **Show Bot Accounts**: Disable this to hide posts that were created by bot accounts. 65 | - **Show Read Posts**: If this is disabled, posts that you already viewed are not shown in listings anymore. Useful if you want to find new content easily, but makes it difficult to follow ongoing discussion under existing posts. 66 | - **Show Notifications for New Posts**: Enable this to receive a popup notification for each new post that is created. 67 | - **Send notifications to Email**: Enable to receive notifications about new comment replies and private messages to your email address. 68 | 69 | ## Start Posting 70 | 71 | Finally its time to start posting! To do this it is always a good idea to read the community rules in the sidebar (below the _Subscribe_ button). When you are ready, go to a post and type your comment in the box directly below for a top-level reply. You can also write a nested reply to an existing comment, by clicking the left-pointing arrow. 72 | 73 | Other than commenting on existing posts, you can also create new posts. To do this, click the button _Create a post_ in the sidebar. Here you can optionally supply an external link or upload an image. The title field is mandatory and should describe what you are posting. The body is again optional, and gives space for long texts. You can also embed additional images here. The _Community_ dropdown below allows choosing a different community to post in. With _NSFW_, posts can be marked as "not safe for work". Finally you can specify the language that the post is written in, and then click on _Create_. 74 | 75 | One more possibility is to write private messages to individual users. To do this, simply visit a user profile and click _Send message_. You will be notified about new private messages and comment replies with the bell icon in the top right. 76 | -------------------------------------------------------------------------------- /src/users/02-media.md: -------------------------------------------------------------------------------- 1 | # Media 2 | 3 | ## Text 4 | 5 | The main type of content in Lemmy is text which can be formatted with Markdown. Refer to the table below for supported formatting rules. The Lemmy user interface also provides buttons for formatting, so it's not necessary to remember all of it. You can also follow the interactive [CommonMark tutorial](https://commonmark.org/help/tutorial/) to get started. 6 | 7 | | Type | Or | … to Get | 8 | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | 9 | | \*Italic\* | \_Italic\_ | _Italic_ | 10 | | \*\*Bold\*\* | \_\_Bold\_\_ | **Bold** | 11 | | \# Heading 1 | Heading 1
========= |

Heading 1

| 12 | | \## Heading 2 | Heading 2
--------- |
Heading 2
| 13 | | \[Link\](http://a.com) | \[Link\]\[1\]

\[1\]: http://b.org | [Link](https://commonmark.org/) | 14 | | !\[Image\](http://url/a.png) | !\[Image\]\[1\]

\[1\]: http://url/b.jpg | ![Markdown](https://commonmark.org/help/images/favicon.png) | 15 | | Example\^\[Footnote\] | | Example[1]

  1. Footnote ↩︎

| 16 | | \> Blockquote | |
Blockquote
| 17 | | \* List
\* List
\* List | \- List
\- List
\- List
| | 18 | | 1\. One
2\. Two
3\. Three | 1) One
2) Two
3) Three |
  1. One
  2. Two
  3. Three
| 19 | | Horizontal Rule
\--- | Horizontal Rule
\*\*\* | Horizontal Rule

| 20 | | \`Inline code\` with backticks | | `Inline code` with backticks | 21 | | \`\`\`
\# code block
print '3 backticks or'
print 'indent 4 spaces'
\`\`\` | ····\# code block
····print '3 backticks or'
····print 'indent 4 spaces' |
# code block
print '3 backticks or'
print 'indent 4 spaces'
| 22 | | ::: spoiler hidden or nsfw stuff
_a bunch of spoilers here_
::: | |
hidden or nsfw stuff

a bunch of spoilers here

| 23 | | Some \~subscript\~ text | | Some subscript text | 24 | | Some \^superscript\^ text | | Some superscript text | 25 | | \~\~Strikethrough\~\~ | | Some ~removed~ text | 26 | | \{Ruby\|text\} | | Rubytext | 27 | 28 | [CommonMark Tutorial](https://commonmark.org/help/tutorial/) 29 | 30 | ## Images and Video 31 | 32 | Lemmy also allows sharing of images and videos. To upload an image, go to the _Create post_ page and click the little image icon under the _URL_ field. This allows you to select a local image. If you made a mistake, a popup message allows you to delete the image. The same image button also allows uploading of videos in .gif format. Instead of uploading a local file, you can also simply paste the URL of an image or video from another website. 33 | 34 | Note that this functionality is not meant to share large images or videos, because that would require too many server resources. Instead, upload them on another platform like [PeerTube](https://joinpeertube.org/) or [Pixelfed](https://pixelfed.org/), and share the link on Lemmy. 35 | 36 | ## Torrents 37 | 38 | Since Lemmy doesn't host large videos or other media, users can share files using [BitTorrent](https://en.wikipedia.org/wiki/BitTorrent) links. In BitTorrent, files are shared not by a single user, but by _many users_ at the same time. This makes file sharing efficient, fast, and reliable, as long as several sources are sharing the files. 39 | 40 | Lemmy supports posting torrent magnet links (links that start with `magnet:`) in the post _URL_ field, or as Markdown links within comments. You can get a magnet link by clicking _copy magnet link_ in your torrent app. 41 | 42 | With this, Lemmy can serve as an alternative to centralized media-centric services like YouTube and Spotify. 43 | 44 | ### How to Watch Torrents 45 | 46 | #### Beginner 47 | 48 | To easily stream videos and audio on Lemmy, you can use any of the following apps. After clicking on a torrent link in Lemmy, a dialog will pop up asking you to open the link in the app. 49 | 50 | - [Stremio](https://www.stremio.com/) (Desktop, Android) 51 | - [WebTorrent Desktop](https://webtorrent.io/desktop/) (Desktop) 52 | - [Popcorn Time](https://github.com/popcorn-official/popcorn-desktop) (Desktop) 53 | - [xTorrent](https://play.google.com/store/apps/details?id=com.gamemalt.streamtorrentvideos) (Android) 54 | 55 | #### Advanced 56 | 57 | For those who would like to help share files, you can use any of the following torrent clients: 58 | 59 | - [qBittorrent](https://qbittorrent.org/) (Desktop) 60 | - [Deluge](https://www.deluge-torrent.org/) (Desktop) 61 | - [Transmission](https://transmissionbt.com/) (Desktop) 62 | - [LibreTorrent](https://gitlab.com/proninyaroslav/libretorrent) (Android) 63 | 64 | Many of these support _streaming_ videos. To do this, make sure you check _sequential download_, wait for enough of the download to complete, then click to open the video file. 65 | 66 | If you'd like, you can also set up a media server to view this content on any device. Some good options are: 67 | 68 | - [Jellyfin](https://jellyfin.org/) (Movies, TV, Music, Audiobooks) 69 | - [Navidrome](https://www.navidrome.org/) (Music) 70 | - [audiobookshelf](https://www.audiobookshelf.org/) (Audiobooks) 71 | -------------------------------------------------------------------------------- /src/administration/horizontal_scaling.md: -------------------------------------------------------------------------------- 1 | # Horizontal Scaling 2 | 3 | This is a collection of notes on scaling different Lemmy components horizontally. This is not meant as a step-by-step guide, rather as general tips and knowledge that might be useful to somebody who is already familiar with and horizontal scaling concepts and best practices. If you don't already know about concepts like load balancing, object storage, and reverse proxies, then this document will probably be hard (or impossible) to understand. 4 | 5 | **Note: scaling Lemmy this way is not necessary, and for small instances, the added overhead most likely outweighs any benefits.** 6 | 7 | ## Why scale horizontally? 8 | 9 | - If one of your Lemmy servers dies for any reason, your instance can still remain operational 10 | - Having multiple Lemmy servers allows you to do rolling upgrades (no downtime needed to upgrade Lemmy!) 11 | - Horizontal scaling provides additional flexibility in how you upgrade your infrastructure 12 | - As of Lemmy version 0.18.2, Lemmy appears to be able to use the same resources more efficiently if they are split between multiple Lemmy processes 13 | 14 | ## Breakdown of Lemmy components 15 | 16 | See [Lemmy components](administration.md#lemmy-components) for a high level overview of what each component does. 17 | 18 | ### Lemmy-ui 19 | 20 | Lemmy-ui can be horizontally scaled, but make sure you read the following section for information about rolling upgrades. 21 | 22 | #### Rolling upgrades 23 | 24 | In general, there is nothing preventing you from running multiple load balanced Lemmy-ui servers in parallel, but without some custom configuration, **rolling upgrades will break Lemmy for your users!** This is because by default, Lemmy-ui will serve static files through the express process. 25 | 26 | Consider the scenario where you have 2 Lemmy-ui servers, one is running on version 1, and the other has just come online running version 2. A user opening your Lemmy in their browser might have their front page html request routed to version 1. This html will contain a reference to many other static files which are specific to version 1. Most likely, some of these requests will get routed to the server running version 2. **The files will not exist on this server, the server will respond with 404, and the UI will remain in a broken state.** 27 | 28 | There are a few ways to work around this issue: 29 | 30 | 1. The safest option is to ensure that all servers are able to serve static files for all currently active Lemmy-ui versions. 31 | - There are many ways to achieve this, the most obvious one being to upload your static files to object storage, and have reverse proxies route static file requests to your static files bucket. 32 | - Here's one possible example of how to do this with nginx: 33 | 34 | ``` 35 | location /static { 36 | expires 1y; 37 | add_header Cache-Control "public"; 38 | proxy_pass https://${bucket_fqdn}; 39 | 40 | limit_except GET { 41 | deny all; 42 | } 43 | 44 | proxy_intercept_errors on; 45 | # invalid keys result in 403 46 | error_page 403 =404 /@404static; 47 | } 48 | ``` 49 | 50 | Replace `${bucket_fqdn}` with the URL to your bucket. With this setup, you should upload the contents of the lemmy-ui `dist/` directory to a "directory" named `static/$LEMMY_UI_HASH` in your bucket, where the hash is calculated from the commit you are deploying: `export LEMMY_UI_HASH=$(git rev-parse --short HEAD)`. Note that this setup requires public acl on uploaded files, so it's recommended to limit access to your bucket with an IP allowlist policy. 51 | 52 | 2. An alternative option is to configure sticky sessions in your load balancer. This will ensure that subsequent requests all end up on the same Lemmy-ui server, and as long as your sticky sessions last longer than your upgrade process, most clients should remain functional. 53 | 3. Similarly to sticky sessions, another possibility is to configure ip hash based load balancing. 54 | 4. There is always the option to just employ downtime and upgrade all servers at once (but that's not very fun!) 55 | 56 | ### Lemmy_server 57 | 58 | Lemmy_server can be horizontally scaled, with a few caveats. 59 | 60 | Here's a quick example on how you could start 3 web servers, 3 federation servers and one scheduled task process: 61 | 62 | ``` 63 | # scheduled tasks 64 | lemmy_server --disable-http-server --disable-activity-sending 65 | ``` 66 | 67 | Or run the Docker container with the `-e LEMMY_DISABLE_HTTP_SERVER=true` and `-e LEMMY_DISABLE_ACTIVITY_SENDING=true` ENV parameters. 68 | 69 | ``` 70 | # 3 http servers 71 | lemmy_server --disable-activity-sending --disable-scheduled-tasks 72 | lemmy_server --disable-activity-sending --disable-scheduled-tasks 73 | lemmy_server --disable-activity-sending --disable-scheduled-tasks 74 | ``` 75 | 76 | Or run the Docker containers with the `-e LEMMY_DISABLE_ACTIVITY_SENDING=true` and `-e LEMMY_DISABLE_SCHEDULED_TASKS=true` ENV parameters. 77 | 78 | ``` 79 | # 3 servers for sending out federation activities 80 | lemmy_server --disable-http-server --disable-scheduled-tasks --federate-process-index=1 --federate-process-count=3 81 | lemmy_server --disable-http-server --disable-scheduled-tasks --federate-process-index=2 --federate-process-count=3 82 | lemmy_server --disable-http-server --disable-scheduled-tasks --federate-process-index=3 --federate-process-count=3 83 | ``` 84 | 85 | Or run the Docker containers with the `-e LEMMY_DISABLE_HTTP_SERVER=true`, `-e LEMMY_DISABLE_SCHEDULED_TASKS=true`, `-e LEMMY_FEDERATE_PROCESS_INDEX=1` and `-e LEMMY_FEDERATE_PROCESS_COUNT=3` ENV parameters. 86 | 87 | #### Scheduled tasks 88 | 89 | By default, a Lemmy_server process will run background scheduled tasks, which must be run only on one server. Launching multiple processes with the default configuration will result in multiple duplicated scheduled tasks all starting at the same moment and trying to do the same thing at once. 90 | 91 | To solve this, Lemmy must be started with the `--disable-scheduled-tasks` flag (or `LEMMY_DISABLE_SCHEDULED_TASKS=true`) on all but one instance. In general, there are two approaches: 92 | 93 | 1. Run all your load balanced Lemmy servers with the `--disable-scheduled-tasks` flag, and run one additional Lemmy server without this flag which is not in your load balancer and does not accept any HTTP traffic. 94 | 2. Run one load balanced Lemmy server without the flag, and all other load balanced servers with the flag. 95 | 96 | #### Federation queue 97 | 98 | The persistent federation queue (since 0.19) is split by federated domain and can be processed in equal-size parts run in separate processes. To split the queue up into N processes numbered 1...N, use the arguments `--federate-process-index=i --federate-process-count=N` on each (or `LEMMY_FEDERATE_PROCESS_INDEX=i` and `LEMMY_FEDERATE_PROCESS_COUNT=N` respectively). It is important that each index is is given to exactly one process, otherwise you will get undefined behaviour (missing, dupe federation, crashes). 99 | 100 | Federation processes can be started and stopped at will. They will restart federation to each instance from the last transmitted activity regardless of downtime. 101 | 102 | #### Rolling upgrades 103 | 104 | For most versions, rolling releases have been completely OK, but there have been some cases where a database migration slips in which is NOT backwards compatible (causing any servers running the older version of Lemmy to potentially start generating errors). To be safe, you should always first take a look at [what database migrations are included in a new version](https://github.com/LemmyNet/lemmy/tree/main/migrations), and evaluate whether the changes are for sure safe to apply with running servers. 105 | 106 | Note that even if there are no backwards incompatible migrations between immediate version upgrades (version 1 -> 2 is safe and 2 -> 3 is safe), doing bigger upgrades might cause these same migrations to become backwards incompatible (a server running version 1 might not be compatible with migrations from version 3 in this scenario). **It is generally best to only upgrade one version at a time!**. 107 | 108 | For the scheduled task process, it shouldn't cause any major issues to have two servers running in parallel for a short duration during an upgrade, but shutting the old one down before starting the new one will always be safer. 109 | 110 | ### Pict-rs 111 | 112 | Pict-rs does not scale horizontally as of writing this document. This is due to a dependency on a Sled database, which is a disk based database file. 113 | 114 | The developer of pict-rs has mentioned plans to eventually add Postgres support (which should in theory enable horizontal scaling), but work on this has not yet started. 115 | 116 | Note that by caching pict-rs images (for example, with nginx, or with a CDN), you can really minimize load on the pict-rs server! 117 | 118 | ## Other tips 119 | 120 | 1. Your lemmy-ui servers need to access your backend as well. If you don't want a complicated setup with multiple load balancers, you could just have each of your servers contain nginx, lemmy-ui, and lemmy_server, and just load balance to an nginx on each of your servers 121 | 2. Make sure you're passing the real client ip address to Lemmy, otherwise Lemmy's built in rate limiter will trigger very often (if it rate limits based on your load balancer's IP address, for example). With nginx, you should use something like this in your nginx.conf: 122 | 123 | ``` 124 | real_ip_recursive on; 125 | real_ip_header X-Forwarded-For; 126 | set_real_ip_from ; 127 | ``` 128 | 129 | 3. The internal Lemmy load balancer works based on an in-memory storage of ip addresses. That means that by adding more Lemmy servers, you are effectively making your rate limits less strict. If you rely on the built in limiter, make sure to adjust the limits accordingly! 130 | -------------------------------------------------------------------------------- /src/administration/tor_hidden_service.md: -------------------------------------------------------------------------------- 1 | # Running a Tor Hidden Service 2 | 3 | This guide assumes Lemmy has been installed using the official [Docker Compose](install_docker.md) method. 4 | 5 | Note that federation is not currently supported over the Tor network. An existing Lemmy instance is required. This procedure will proxy Lemmy though Tor, but federation tasks are still handled by HTTPS on the open internet. 6 | 7 | [Tor](https://torproject.org) ("The Onion Router") is software designed to circumvent censorship and prevent bad actors from monitoring your activity on the internet by encrypting and distributing network traffic through a decentralized pool of relay servers run by volunteers all over the world. 8 | 9 | A Tor hidden service is only accessible through the Tor network using the `.onion` top-level domain with the official [Tor Browser](https://www.torproject.org/download/), or any client capable of communicating over a SOCKS5 proxy. Hosting a service on the Tor network is a good way to promote digital privacy and internet freedom. 10 | 11 | # Installing Tor 12 | 13 | The official [documentation](https://support.torproject.org/apt/tor-deb-repo/) suggests Ubuntu and Debian users install Tor from the `deb.torproject.org` repository because it always provides the latest stable release of the software. 14 | 15 | **Administrative Access is Required** 16 | 17 | Commands below are expected to be executed as the `root` user. To become root use `sudo -i` or `su -`. 18 | 19 | With `sudo`: 20 | 21 | ``` 22 | sudo -i 23 | Password: [authenticate with current user password] 24 | ``` 25 | 26 | With `su`: 27 | 28 | ``` 29 | su - 30 | Password: [authenticate with root's password] 31 | ``` 32 | 33 | Note: To return to your account run: `exit`. 34 | 35 | **Verify your architecture is supported** 36 | 37 | The package repository only supports `amd64`, `arm64`, and `i386` architectures. 38 | 39 | ``` 40 | dpkg --print-architecture 41 | ``` 42 | 43 | If your architecture is not supported you may want to consider installing Tor [from source](https://community.torproject.org/onion-services/setup/install/#installing-tor-from-source). 44 | 45 | **Install prerequisite packages** 46 | 47 | ``` 48 | apt install -y apt-transport-https ca-certificates gpg lsb-release wget 49 | ``` 50 | 51 | **Enable the deb.torproject.org repository** 52 | 53 | Configure `apt` to pull packages from `deb.torproject.org`. 54 | 55 | ``` 56 | bash -c 'dist=$(lsb_release -s -c); /bin/echo -e \ 57 | "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] \ 58 | https://deb.torproject.org/torproject.org $dist main\n\ 59 | deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] \ 60 | https://deb.torproject.org/torproject.org $dist main" \ 61 | > /etc/apt/sources.list.d/tor.list' 62 | ``` 63 | 64 | **Import deb.torproject.org's GPG signing key** 65 | 66 | ``` 67 | wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc \ 68 | | gpg --dearmor \ 69 | | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null 70 | ``` 71 | 72 | The signing key ensures the package retrieved from the server was created by `deb.torproject.org`. 73 | 74 | **Install tor** 75 | 76 | ``` 77 | apt update && apt install -y tor 78 | ``` 79 | 80 | ## Creating a Tor hidden service 81 | 82 | Create a new hidden service directory: 83 | 84 | ``` 85 | mkdir /var/lib/tor/hidden_lemmy_service 86 | ``` 87 | 88 | Append the following to `/etc/tor/torrc` to tie the hidden service directory to the `tor` daemon: 89 | 90 | ``` 91 | HiddenServiceDir /var/lib/tor/hidden_lemmy_service/ 92 | HiddenServicePort 80 127.0.0.1:10080 93 | ``` 94 | 95 | `HiddenServiceDir [path]` is where `tor` will store data related to the hidden service, and `HiddenServicePort [hidden_service_port] [host_ip:port]` binds a port on the host to a hidden service port on the Tor network. 96 | 97 | ## Enable and start the Tor daemon 98 | 99 | ``` 100 | systemctl enable --now tor 101 | ``` 102 | 103 | At startup `tor` daemon will automatically populate `/var/lib/tor/hidden_lemmy_service/` with encryption keys, certificates, and assign a hostname for the new service. 104 | 105 | ## Determine your hidden service's hostname 106 | 107 | ``` 108 | cat /var/lib/tor/hidden_lemmy_service/hostname 109 | ``` 110 | 111 | The `.onion` address contained in this file will be referred to as `HIDDEN_SERVICE_ADDR` from here on. 112 | 113 | # Configure your existing Lemmy instance 114 | 115 | ## Docker compose 116 | 117 | Forward port `10080` from the `proxy` container to the hidden service port `127.0.0.1:10080`. This exposes `10080/tcp` to the local host, and will not be directly accessible from the internet. For context `"80:80"` binds port `80/tcp` (HTTP) to `0.0.0.0:80` on the host. Unless a firewall is configured to block incoming traffic to `80` this will be exposed to other hosts on the local area network (LAN) and/or the open internet. 118 | 119 | **docker-compose.yml** 120 | 121 | ``` 122 | services: 123 | # ... 124 | proxy: 125 | # ... 126 | ports: 127 | - "80:80" 128 | - "443:443" 129 | - "127.0.0.1:10080:10080" 130 | ``` 131 | 132 | ## Configure NGINX 133 | 134 | Append a new `server {...}` block to handle tor traffic, and add the `Onion-Location` header to the SSL encrypted server exposed to the internet. This header informs Tor Browser users that an equivalent `.onion` site exists on the Tor network by displaying an icon next to the address bar. 135 | 136 | **nginx.conf** 137 | 138 | ``` 139 | worker_processes 1; 140 | events { 141 | worker_connections 1024; 142 | } 143 | 144 | http { 145 | # Original configuration listening on port 80 146 | server { 147 | listen 80; 148 | # ... 149 | } 150 | 151 | # Original configuration listening on port 443 152 | server { 153 | listen 443; 154 | # ... 155 | location / { 156 | # Handle Tor Browser's ".onion" link detection 157 | add_header Onion-Location "http://HIDDEN_SERVICE_ADDR$request_uri" always; 158 | # ... 159 | } 160 | } 161 | 162 | # Establish a rate limit for the hidden service address 163 | limit_req_zone $binary_remote_addr zone=HIDDEN_SERVICE_ADDR_ratelimit:10m rate=1r/s; 164 | 165 | # Add tor-specific upstream aliases as a visual aid to 166 | # avoid editing the incorrect server block in the future 167 | upstream lemmy-tor { 168 | server "lemmy:8536"; 169 | } 170 | upstream lemmy-ui-tor { 171 | server "lemmy-ui:1234"; 172 | } 173 | 174 | # Add a copy of your current internet-facing configuration with 175 | # "listen" and "server_listen" modified to send all traffic 176 | # over the Tor network, incorporating the visual upstream aliases 177 | # above. 178 | server { 179 | # Tell nginx to listen on the hidden service port 180 | listen 10080; 181 | 182 | # Set server_name to the contents of the file: 183 | # /var/lib/tor/hidden_lemmy_service/hostname 184 | server_name HIDDEN_SERVICE_ADDR; 185 | 186 | # Hide nginx version 187 | server_tokens off; 188 | 189 | # Enable compression for JS/CSS/HTML bundle, for improved client load times. 190 | # It might be nice to compress JSON, but leaving that out to protect against potential 191 | # compression+encryption information leak attacks like BREACH. 192 | gzip on; 193 | gzip_types text/css application/javascript image/svg+xml; 194 | gzip_vary on; 195 | 196 | # Various content security headers 197 | add_header Referrer-Policy "same-origin"; 198 | add_header X-Content-Type-Options "nosniff"; 199 | add_header X-Frame-Options "DENY"; 200 | add_header X-XSS-Protection "1; mode=block"; 201 | 202 | # Upload limit for pictrs 203 | client_max_body_size 20M; 204 | 205 | # frontend 206 | location / { 207 | # distinguish between ui requests and backend 208 | # don't change lemmy-ui or lemmy here, they refer to the upstream definitions on top 209 | set $proxpass "http://lemmy-ui-tor"; 210 | 211 | if ($http_accept = "application/activity+json") { 212 | set $proxpass "http://lemmy-tor"; 213 | } 214 | if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") { 215 | set $proxpass "http://lemmy-tor"; 216 | } 217 | if ($request_method = POST) { 218 | set $proxpass "http://lemmy-tor"; 219 | } 220 | proxy_pass $proxpass; 221 | 222 | rewrite ^(.+)/+$ $1 permanent; 223 | 224 | # Send actual client IP upstream 225 | proxy_set_header X-Real-IP $remote_addr; 226 | proxy_set_header Host $host; 227 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 228 | } 229 | 230 | # backend 231 | location ~ ^/(api|feeds|nodeinfo|.well-known) { 232 | proxy_pass "http://lemmy-tor"; 233 | proxy_http_version 1.1; 234 | proxy_set_header Upgrade $http_upgrade; 235 | proxy_set_header Connection "upgrade"; 236 | 237 | # Rate limit 238 | limit_req zone=HIDDEN_SERVICE_ADDR_ratelimit burst=30 nodelay; 239 | 240 | # Add IP forwarding headers 241 | proxy_set_header X-Real-IP $remote_addr; 242 | proxy_set_header Host $host; 243 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 244 | } 245 | 246 | # pictrs only - for adding browser cache control. 247 | location ~ ^/(pictrs) { 248 | # allow browser cache, images never update, we can apply long term cache 249 | expires 120d; 250 | add_header Pragma "public"; 251 | add_header Cache-Control "public"; 252 | 253 | proxy_pass "http://lemmy-tor"; 254 | proxy_http_version 1.1; 255 | proxy_set_header Upgrade $http_upgrade; 256 | proxy_set_header Connection "upgrade"; 257 | 258 | # Rate limit 259 | limit_req zone=HIDDEN_SERVICE_ADDR_ratelimit burst=30 nodelay; 260 | 261 | # Add IP forwarding headers 262 | proxy_set_header X-Real-IP $remote_addr; 263 | proxy_set_header Host $host; 264 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 265 | } 266 | 267 | # Redirect pictshare images to pictrs 268 | location ~ /pictshare/(.*)$ { 269 | return 301 /pictrs/image/$1; 270 | } 271 | } 272 | 273 | # ... 274 | } 275 | ``` 276 | 277 | # Apply the configuration(s) 278 | 279 | Restart all services associated with your Lemmy instance: 280 | 281 | ``` 282 | docker compose down 283 | docker compose up -d 284 | ``` 285 | 286 | # Test connectivity over Tor 287 | 288 | Using `torsocks`, verify your hidden service is available on the Tor network. 289 | 290 | ``` 291 | torsocks curl -vI http://HIDDEN_SERVICE_ADDR 292 | * Trying 127.*.*.*:80... 293 | * Connected to HIDDEN_SERVICE_ADDR (127.*.*.*) port 80 (#0) 294 | > HEAD / HTTP/1.1 295 | > Host: HIDDEN_SERVICE_ADDR 296 | > User-Agent: curl/7.76.1 297 | > Accept: */* 298 | > 299 | * Mark bundle as not supporting multiuse 300 | < HTTP/1.1 200 OK 301 | HTTP/1.1 200 OK 302 | < Server: nginx 303 | Server: nginx 304 | < Date: Wed, 07 Jun 2023 17:06:00 GMT 305 | Date: Wed, 07 Jun 2023 17:06:00 GMT 306 | < Content-Type: text/html; charset=utf-8 307 | Content-Type: text/html; charset=utf-8 308 | < Content-Length: 98487 309 | Content-Length: 98487 310 | < Connection: keep-alive 311 | Connection: keep-alive 312 | < Vary: Accept-Encoding 313 | Vary: Accept-Encoding 314 | < X-Powered-By: Express 315 | X-Powered-By: Express 316 | < Content-Security-Policy: default-src 'self'; manifest-src *; connect-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; form-action 'self'; base-uri 'self'; frame-src * 317 | Content-Security-Policy: default-src 'self'; manifest-src *; connect-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; form-action 'self'; base-uri 'self'; frame-src * 318 | < ETag: W/"180b7-EC9iFYAIlbnN8zHCayBwL3wAm64" 319 | ETag: W/"180b7-EC9iFYAIlbnN8zHCayBwL3wAm64" 320 | < Referrer-Policy: same-origin 321 | Referrer-Policy: same-origin 322 | < X-Content-Type-Options: nosniff 323 | X-Content-Type-Options: nosniff 324 | < X-Frame-Options: DENY 325 | X-Frame-Options: DENY 326 | < X-XSS-Protection: 1; mode=block 327 | X-XSS-Protection: 1; mode=block 328 | 329 | < 330 | * Connection #0 to host HIDDEN_SERVICE_ADDR left intact 331 | ``` 332 | 333 | ## Logging behavior 334 | 335 | Hidden service traffic will appear to originate from the `lemmyexternalproxy` docker network instead of an internet IP. Docker's default network address pool is `172.17.0.0/16`. 336 | 337 | ``` 338 | docker compose logs -f proxy 339 | lemmy-proxy-1 | 172.*.0.1 - - # ... 340 | lemmy-proxy-1 | 172.*.0.1 - - # ... 341 | lemmy-proxy-1 | 172.*.0.1 - - # ... 342 | ``` 343 | -------------------------------------------------------------------------------- /src/administration/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | Different problems that can occur on a new instance, and how to solve them. 4 | 5 | Many Lemmy features depend on a correct reverse proxy configuration. Make sure yours is equivalent to our [nginx config](https://github.com/LemmyNet/lemmy-ansible/blob/main/templates/nginx.conf). 6 | 7 | ## General 8 | 9 | ### Logs 10 | 11 | For frontend issues, check the [browser console](https://webmasters.stackexchange.com/a/77337) for any error messages. 12 | 13 | For server logs, run `docker compose logs -f lemmy` in your installation folder. You can also do `docker compose logs -f lemmy lemmy-ui pictrs` to get logs from different services. 14 | 15 | If that doesn't give enough info, try changing the line `RUST_LOG=error` in `docker-compose.yml` to `RUST_LOG=info` or `RUST_LOG=trace`, then do `docker compose restart lemmy`. 16 | 17 | ### Creating admin user doesn't work 18 | 19 | Make sure that websocket is working correctly, by checking the browser console for errors. In nginx, the following headers are important for this: 20 | 21 | ``` 22 | proxy_http_version 1.1; 23 | proxy_set_header Upgrade $http_upgrade; 24 | proxy_set_header Connection "upgrade"; 25 | ``` 26 | 27 | ### Rate limit error when many users access the site 28 | 29 | Check that the headers `X-Real-IP` and `X-Forwarded-For` are sent to Lemmy by the reverse proxy. Otherwise, it will count all actions towards the rate limit of the reverse proxy's IP. In nginx it should look like this: 30 | 31 | ``` 32 | proxy_set_header X-Real-IP $remote_addr; 33 | proxy_set_header Host $host; 34 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 35 | ``` 36 | 37 | ## Federation 38 | 39 | ### Other instances can't fetch local objects (community, post, etc) 40 | 41 | Your reverse proxy (eg nginx) needs to forward requests with header `Accept: application/activity+json` to the backend. This is handled by the following lines: 42 | 43 | ``` 44 | set $proxpass "http://0.0.0.0:{{ lemmy_ui_port }}"; 45 | if ($http_accept = "application/activity+json") { 46 | set $proxpass "http://0.0.0.0:{{ lemmy_port }}"; 47 | } 48 | if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") { 49 | set $proxpass "http://0.0.0.0:{{ lemmy_port }}"; 50 | } 51 | proxy_pass $proxpass; 52 | ``` 53 | 54 | You can test that it works correctly by running the following commands, all of them should return valid JSON: 55 | 56 | ``` 57 | curl -H "Accept: application/activity+json" https://your-instance.com/u/some-local-user 58 | curl -H "Accept: application/activity+json" https://your-instance.com/c/some-local-community 59 | curl -H "Accept: application/activity+json" https://your-instance.com/post/123 # the id of a local post 60 | curl -H "Accept: application/activity+json" https://your-instance.com/comment/123 # the id of a local comment 61 | ``` 62 | 63 | ### Fetching remote objects works, but posting/commenting in remote communities fails 64 | 65 | Check that [federation is allowed on both instances](federation_getting_started.md). 66 | 67 | Also ensure that the time is correct on your server. Activities are signed with a timestamp, and will be discarded if it is off by more than one hour. 68 | 69 | It is possible that federation requests to `/inbox` are blocked by tools such as Cloudflare. The sending instance can find HTTP errors with the following steps: 70 | 71 | - If you use a [separate container for outgoing federation](horizontal_scaling.md), you need to apply the following steps to that container only 72 | - Set `RUST_LOG=lemmy_federate=trace` for Lemmy 73 | - Reload the new configuration: `docker compose up -d` 74 | - Search for messages containing the target instance domain: `docker compose logs -f --tail=100 lemmy | grep -F lemm.ee -C 10` 75 | - You also may have to reset the fail count for the target instance (see below) 76 | 77 | ### Reset federation fail count for instance 78 | 79 | If federation sending to a specific instance has been failing consistently, Lemmy will slow down sending using exponential backoff. For testing it can be useful to reset this and make Lemmy send activities immediately. To do this use the following steps: 80 | 81 | - Stop Lemmy, or specifically the container for outgoing federation `docker compose stop lemmy` 82 | - Enter SQL command line: `sudo docker compose exec postgres psql -U lemmy` 83 | - Reset failure count via SQL: 84 | 85 | ```sql 86 | update federation_queue_state 87 | set fail_count = 0 88 | from instance 89 | where instance.id = federation_queue_state.instance_id 90 | and instance.domain = 'lemm.ee'; 91 | ``` 92 | 93 | - Exit SQL command line with `\q`, then restart Lemmy: `docker compose start lemmy` 94 | 95 | ### Other instances don't receive actions reliably 96 | 97 | Lemmy uses one queue per federated instance to send out activities. Search the logs for "Federation state" for summaries. Errors will also be logged. 98 | 99 | For details, execute this SQL query: 100 | 101 | ```sql 102 | select domain,currval('sent_activity_id_seq') as latest_id, last_successful_id,fail_count,last_retry from federation_queue_state 103 | join instance on instance_id = instance.id order by last_successful_id asc; 104 | ``` 105 | 106 | You will see a table like the following: 107 | 108 | | domain | latest_id | last_successful_id | fail_count | last_retry | 109 | | -------------------------- | --------- | ------------------ | ---------- | ----------------------------- | 110 | | toad.work | 6837196 | 6832351 | 14 | 2023-07-12 21:42:22.642379+00 | 111 | | lemmy.deltaa.xyz | 6837196 | 6837196 | 0 | 1970-01-01 00:00:00+00 | 112 | | battleangels.net | 6837196 | 6837196 | 0 | 1970-01-01 00:00:00+00 | 113 | | social.fbxl.net | 6837196 | 6837196 | 0 | 1970-01-01 00:00:00+00 | 114 | | mastodon.coloradocrest.net | 6837196 | 6837196 | 0 | 1970-01-01 00:00:00+00 | 115 | 116 | This will show you exactly which instances are up to date or not. 117 | 118 | You can also use this website which will help you monitor this without admin rights, and also let others see it 119 | 120 | https://phiresky.github.io/lemmy-federation-state/site 121 | 122 | ### You don't receive actions reliably 123 | 124 | Due to the lemmy queue, remote lemmy instances will be sending apub sync actions serially to you. If your server rate of processing them is slower than the rate the origin server is sending them, when visiting the [lemmy-federation-state](https://phiresky.github.io/lemmy-federation-state/site) for the remote server, you'll see your instance in the "lagging behind" section. 125 | 126 | This can be avoided by setting the config value `federation.concurrent_sends_per_instance` to a value greater than 1 on the sending instance. 127 | 128 | Typically the speed at which you process an incoming action should be less than 100ms. If this is higher, this might signify problems with your database performance or your networking setup. 129 | 130 | Note that even apub action ingestion speed which seems sufficient for most other instances, might become insufficient if the origin server is receiving actions from their userbase faster than you can process them. I.e. if the origin server receives 10 actions per second, but you can only process 8 actions per second, you'll inevitably start falling behind that one server only. 131 | 132 | These steps might help you diagnose this. 133 | 134 | #### Check processing time on the loadbalancer 135 | 136 | Check how long a request takes to process on the backend. In haproxy for example, the following command will show you the time it takes for apub actions to complete 137 | 138 | ```bash 139 | tail -f /var/log/haproxy.log | grep "POST \/inbox" 140 | ``` 141 | 142 | [See here for nginx](https://www.nginx.com/blog/using-nginx-logging-for-application-performance-monitoring/) 143 | 144 | If these actions take more than 100ms, you might want to investigate deeper. 145 | 146 | #### Check your Database performance 147 | 148 | Ensure that it's not very high in CPU or RAM utilization. 149 | 150 | Afterwards check for slow queries. If you regularly see common queries with high max and mean exec time, it might signify your database is struggling. The below SQL query will show you all queries (you will need `pg_stat_statements` [enabled](https://www.postgresql.org/docs/current/pgstatstatements.html)) 151 | 152 | ```sql 153 | \x auto 154 | SELECT user,query,max_exec_time,mean_exec_time,calls FROM pg_stat_statements WHERE max_exec_time > 10 AND CALLS > 100 ORDER BY max_exec_time DESC; 155 | ``` 156 | 157 | If you see very high time on inserts, you might want to consider disabling `synchronous_commit` to see if this helps. 158 | 159 | #### Check your backend performance. 160 | 161 | Like the DB, if the server where your lemmy rust backend is running is overloaded, you might see such an impact 162 | 163 | #### Check your Network Layout 164 | 165 | If your backend and database appear to be in good condition, it might be that your issue is network based. 166 | 167 | One problem can occur is your backend and your database are not in the same server and are too far from each other in geographic location. Due to the amount of DB queries performed for each apub sync request, even a small amount of latency can quickly add up. 168 | 169 | Check the latency between your rust backend and your DB using ping 170 | 171 | ```bash 172 | ping your_database_ip 173 | ``` 174 | 175 | if the `time` you see if above 1-2ms, this can start causing such delays. In that case, you might want to consider moving your backend closer to your DB geographically, so that your latency is below 2ms 176 | 177 | Note that your external loadbalancer(s) (if any) do not necessarily need to be closer to the DB, as they do not do multiple small DB requests. 178 | 179 | ## Downgrading 180 | 181 | If you upgraded your instance to a newer version (by mistake or planned) and need to downgrade it. Often you need to reverse database changes as well. 182 | 183 | First you need to figure out what SQL changes happened between your upgraded version, and the one you're downgrading. Then in that diff, check which files were added in the `migrations` dir. 184 | 185 | Let's say that for the migration you're doing, the following were added 186 | 187 | ``` 188 | 2023-10-24-131607_proxy_links 189 | 2023-10-27-142514_post_url_content_type 190 | 2023-12-19-210053_tolerable-batch-insert-speed 191 | 2023-12-22-040137_make-mixed-sorting-directions-work-with-tuple-comparison 192 | 2024-01-05-213000_community_aggregates_add_local_subscribers 193 | 2024-01-15-100133_local-only-community 194 | 2024-01-22-105746_lemmynsfw-changes 195 | 2024-01-25-151400_remove_auto_resolve_report_trigger 196 | 2024-02-15-171358_default_instance_sort_type 197 | 2024-02-27-204628_add_post_alt_text 198 | 2024-02-28-144211_hide_posts 199 | ``` 200 | 201 | Each of these folders contains a `down.sql` file. We need to run that against our postgresql DB to rollback those DB changes. 202 | 203 | 1. Stop your lemmy backend, and take a backup of your DB. 204 | 1. Copy the `migrations` folder to your DB container or server 205 | 1. Acquire a shell in your postgresql container or server and switch to the `postgres` user 206 | 1. Run each relevant script with this command 207 | ```bash 208 | downfolder=2024-02-28-144211_hide_posts 209 | psql -d lemmy -a -f /path/to/migrations/${downfolder}/down.sql 210 | ``` 211 | Alternatively, copy the content of the file and paste into a psql session 212 | 1. You now need to clean the `__diesel_schema_migrations` table from the migration records, so that they will be correctly applied the next time you upgrade. You can use this command to sort them 213 | ```sql 214 | select * from __diesel_schema_migrations ORDER BY run_on ASC; 215 | ``` 216 | You have to delete the entries in that table which match the current timestamp you applied them (This should typically be any time in the past few minutes) 217 | ```sql 218 | delete from __diesel_schema_migrations where version='20240228144211'; 219 | ``` 220 | 1. You should now be able to start your lemmy in the previous version 221 | 222 | ## UI randomly slow or offline 223 | 224 | If you notice that your lemmy-ui sometimes becomes sluggish or unresponsive over a period of minutes/hours and then it passes, you might be getting targeted by scraping bots. 225 | 226 | There's a lot of scraping bots online and they can easily overwhelm your site when they're behaving too "greedily". Unfortunately the existing lemmy-ui has a habit of falling over when polled too eagerly, while the backend still continues to work. 227 | 228 | A solution is to cache responses in nginx as seen in lemmy-ansible [here](https://github.com/LemmyNet/lemmy-ansible/blob/1.5.3/templates/nginx.conf#L2-L3) and [here](https://github.com/LemmyNet/lemmy-ansible/blob/1.5.3/templates/nginx.conf#L66-L71). This way lemmy-ui doesn't have to generate all responses from scratch, which reduces CPU load. However it won't help if a single crawler goes through thousands of unique urls in a short time. 229 | 230 | Another option is to block the scraper's user agents. To do so, you can modify your `nginx_internal.conf` to block some of the usual suspects, with this line under `server` 231 | 232 | ```bash 233 | if ($http_user_agent ~* " Bytedance|Bytespider|Amazonbot|ClaudeBot") { return 444; } 234 | ``` 235 | 236 | This is an example blocking some the well-known misbehaving bots, but there are many more more. To discover the ones affecting you, you can use the following bash script at your lemmy backend (where your docker compose is) to enumerate any agents which are hitting you too much. 237 | 238 | ```bash 239 | docker-compose logs --tail=10000 proxy | 240 | grep -o '"[^"]*"$' | # Extract the last quoted string (user agent) 241 | grep -v '^"$' | # Remove empty quotes 242 | tr -d '"' | # Remove the quotes 243 | sort | # Sort the user agents 244 | uniq -c | # Count unique occurrences 245 | sort -rn | # Sort numerically in reverse order 246 | head -n 10 # Show top 10 results 247 | ``` 248 | 249 | This will parse the last 10K log entries in your nginx internal proxy and show the agents which cause the most hits. This should give a good indicator of which agents are potentially misbehaving and you can proceed to block those as well by adding their names to the list above. 250 | -------------------------------------------------------------------------------- /src/administration/from_scratch.md: -------------------------------------------------------------------------------- 1 | # Install from Scratch 2 | 3 | These instructions are written for Ubuntu 20.04 / Ubuntu 22.04. They are particularly useful when you'd like to setup a Lemmy container (e.g. LXC on Proxmox) and cannot use Docker. 4 | 5 | Lemmy is built from source in this guide, so this may take a while, especially on slow devices. For example, Lemmy v0.18.5 takes around 7 minutes to build on a quad core VPS. 6 | 7 | Installing and configuring Lemmy using this guide takes about 60-90 minutes. You might need to make yourself a fresh cup of coffee before you start. 8 | 9 | ## Installation 10 | 11 | ### Database 12 | 13 | For Ubuntu 20.04 the shipped PostgreSQL version is 12 which is not supported by Lemmy. So let's set up a newer one. 14 | The most recent stable version of PostgreSQL is 16 at the time of writing this guide. 15 | 16 | #### Install dependencies 17 | 18 | ``` 19 | sudo apt install -y wget ca-certificates pkg-config 20 | wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - 21 | sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" >> /etc/apt/sources.list.d/pgdg.list' 22 | sudo apt update 23 | sudo apt install libssl-dev libpq-dev postgresql 24 | ``` 25 | 26 | #### Setup Lemmy database 27 | 28 | Replace db-passwd with a unique password of your choice in the commands below. 29 | 30 | ``` 31 | sudo -iu postgres psql -c "CREATE USER lemmy WITH PASSWORD 'db-passwd';" 32 | sudo -iu postgres psql -c "CREATE DATABASE lemmy WITH OWNER lemmy;" 33 | ``` 34 | 35 | If you're migrating from an older version of Lemmy, the following might be required. 36 | 37 | ``` 38 | sudo -iu postgres psql -c "ALTER USER lemmy WITH SUPERUSER;" 39 | ``` 40 | 41 | Tune your PostgreSQL settings to match your hardware via [this guide](https://pgtune.leopard.in.ua/#/) 42 | 43 | #### Setup md5 auth 44 | 45 | Your Postgres config might need to be edited to allow password authentication instead of peer authentication. Simply add the following to your `pg_hba.conf`: 46 | 47 | ``` 48 | local lemmy lemmy md5 49 | ``` 50 | 51 | ### Install Rust 52 | 53 | For the Rust compiles, it is ideal to use a non-privileged Linux account on your system. Install Rust by following the instructions on [Rustup](https://rustup.rs/) (using a non-privileged Linux account, it will install file in that user's home folder for rustup and cargo). 54 | 55 | protobuf-compiler may be required for Ubuntu 20.04 or 22.04 installs, please report testing in lemmy-docs issues. 56 | 57 | ``` 58 | sudo apt install protobuf-compiler gcc 59 | ``` 60 | 61 | ### Setup pict-rs (Optional) 62 | 63 | You can skip this section if you don't require image hosting, but **NOTE that Lemmy-ui will still allow users to attempt uploading images even if pict-rs is not configured. In this situation, the upload will fail and users will receive technical error messages.** 64 | 65 | Lemmy supports image hosting using [pict-rs](https://git.asonix.dog/asonix/pict-rs/). We need to install a couple of dependencies for this. It requires the `magick` command which comes with Imagemagick version 7, but Ubuntu 20.04 only comes with Imagemagick 6. So you need to install that command manually, eg from the [official website](https://imagemagick.org/script/download.php#linux). 66 | 67 | **NOTE: on standard LXC containers an AppImage-based ImageMagick installation [will not work properly](https://github.com/LemmyNet/lemmy/issues/4112). It uses FUSE which will emit "permission denied" errors when trying to upload an image through pict-rs. You must use alternative installation methods, such as [imei.sh](https://github.com/SoftCreatR/imei).** 68 | 69 | #### AppImage-based installation of ImageMagick 70 | 71 | ``` 72 | sudo apt install ffmpeg exiftool libgexiv2-dev --no-install-recommends 73 | # save the file to a working folder it can be verified before copying to /usr/bin/ 74 | wget https://download.imagemagick.org/ImageMagick/download/binaries/magick 75 | # compare hash with the "message digest" on the official page linked above 76 | sha256sum magick 77 | sudo mv magick /usr/bin/ 78 | sudo chmod 755 /usr/bin/magick 79 | ``` 80 | 81 | #### imei.sh-based installation of ImageMagick 82 | 83 | Follow the instructions from the [official imei.sh page on GitHub](https://github.com/SoftCreatR/imei) 84 | 85 | #### Standalone pict-rs installation 86 | 87 | Since we're building stuff from source here, let's do the same for pict-rs. Follow the [instructions here](https://git.asonix.dog/asonix/pict-rs/#user-content-compile-from-source). 88 | 89 | ### Lemmy Backend 90 | 91 | #### Build the backend 92 | 93 | Create user account on Linux for the lemmy_server application 94 | 95 | ``` 96 | sudo adduser lemmy --system --disabled-login --no-create-home --group 97 | ``` 98 | 99 | Compile and install Lemmy, given the from-scratch intention, this will be done via GitHub checkout. This can be done by a normal unprivledged user (using the same Linux account you used for rustup). 100 | 101 | ```bash 102 | git clone https://github.com/LemmyNet/lemmy.git lemmy 103 | cd lemmy 104 | git checkout 0.18.5 105 | git submodule init 106 | git submodule update 107 | ``` 108 | 109 | Then build Lemmy with the following command: 110 | 111 | ``` 112 | cargo build --release 113 | ``` 114 | 115 | #### Deployment 116 | 117 | Because we should [follow the Linux way](https://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/opt.html), we should use the `/opt` directory to colocate the backend, frontend and pict-rs. 118 | 119 | ``` 120 | sudo mkdir /opt/lemmy 121 | sudo mkdir /opt/lemmy/lemmy-server 122 | sudo mkdir /opt/lemmy/pictrs 123 | sudo mkdir /opt/lemmy/pictrs/files 124 | sudo mkdir /opt/lemmy/pictrs/sled-repo 125 | sudo mkdir /opt/lemmy/pictrs/old 126 | sudo chown -R lemmy:lemmy /opt/lemmy 127 | ``` 128 | 129 | Note that it might not be the most obvious thing, but **creating the pictrs directories is not optional**. 130 | 131 | Then copy the binary. 132 | 133 | ``` 134 | sudo cp target/release/lemmy_server /opt/lemmy/lemmy-server/lemmy_server 135 | ``` 136 | 137 | #### Configuration 138 | 139 | This is the minimal Lemmy config, put this in `/opt/lemmy/lemmy-server/lemmy.hjson` (see [here](https://github.com/LemmyNet/lemmy/blob/main/config/defaults.hjson) for more config options). 140 | 141 | ```hjson 142 | { 143 | database: { 144 | # put your db-passwd from above 145 | password: "db-passwd" 146 | } 147 | # replace with your domain 148 | hostname: example.com 149 | bind: "127.0.0.1" 150 | federation: { 151 | enabled: true 152 | } 153 | # remove this block if you don't require image hosting 154 | pictrs: { 155 | url: "http://localhost:8080/" 156 | } 157 | } 158 | ``` 159 | 160 | Set the correct owner 161 | 162 | ``` 163 | chown -R lemmy:lemmy /opt/lemmy/ 164 | ``` 165 | 166 | #### Server daemon 167 | 168 | Add a systemd unit file, so that Lemmy automatically starts and stops, logs are handled via journalctl etc. Put this file into /etc/systemd/system/lemmy.service. 169 | 170 | ``` 171 | [Unit] 172 | Description=Lemmy Server 173 | After=network.target 174 | 175 | [Service] 176 | User=lemmy 177 | ExecStart=/opt/lemmy/lemmy-server/lemmy_server 178 | Environment=LEMMY_CONFIG_LOCATION=/opt/lemmy/lemmy-server/lemmy.hjson 179 | Environment=PICTRS_ADDR=127.0.0.1:8080 180 | Environment=RUST_LOG="info" 181 | Restart=on-failure 182 | WorkingDirectory=/opt/lemmy 183 | 184 | # Hardening 185 | ProtectSystem=yes 186 | PrivateTmp=true 187 | MemoryDenyWriteExecute=true 188 | NoNewPrivileges=true 189 | 190 | [Install] 191 | WantedBy=multi-user.target 192 | ``` 193 | 194 | If you need debug output in the logs, change the RUST_LOG line in the file above to 195 | 196 | ``` 197 | Environment=RUST_LOG="debug,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" 198 | ``` 199 | 200 | Then run 201 | 202 | ``` 203 | sudo systemctl daemon-reload 204 | sudo systemctl enable lemmy 205 | sudo systemctl start lemmy 206 | ``` 207 | 208 | If you did everything right, the Lemmy logs from `sudo journalctl -u lemmy` should show "Starting http server at 127.0.0.1:8536". You can also run `curl localhost:8536/api/v3/site` which should give a successful response, looking like `{"site_view":null,"admins":[],"banned":[],"online":0,"version":"unknown version","my_user":null,"federated_instances":null}`. For pict-rs, run `curl 127.0.0.1:8080` and ensure that it outputs nothing (particularly no errors). 209 | 210 | ### Lemmy Front-end (lemmy-ui) 211 | 212 | #### Install dependencies 213 | 214 | Nodejs in Ubuntu 20.04 / Ubuntu 22.04 repos are too old, so let's install Node 20. 215 | 216 | ``` 217 | # nodejs 218 | sudo apt install -y ca-certificates curl gnupg 219 | sudo mkdir -p /etc/apt/keyrings 220 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg 221 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list 222 | 223 | sudo apt update 224 | sudo apt install nodejs 225 | 226 | # pnpm 227 | npm i -g pnpm 228 | 229 | ``` 230 | 231 | #### Build the front-end 232 | 233 | Clone the git repo, checkout the version you want (0.18.5 in this case), and compile it. 234 | 235 | ``` 236 | # dont compile as admin 237 | cd /opt/lemmy 238 | sudo -u lemmy bash 239 | git clone https://github.com/LemmyNet/lemmy-ui.git --recursive 240 | cd lemmy-ui 241 | git checkout 0.18.5 # replace with the version you want to install 242 | pnpm i 243 | pnpm build:prod 244 | exit 245 | ``` 246 | 247 | #### UI daemon 248 | 249 | Add another systemd unit file, this time for lemmy-ui. You need to replace `example.com` with your actual domain. Put the file in `/etc/systemd/system/lemmy-ui.service` 250 | 251 | ``` 252 | [Unit] 253 | Description=Lemmy UI 254 | After=lemmy.service 255 | Before=nginx.service 256 | 257 | [Service] 258 | User=lemmy 259 | WorkingDirectory=/opt/lemmy/lemmy-ui 260 | ExecStart=/usr/bin/node dist/js/server.js 261 | Environment=LEMMY_UI_LEMMY_INTERNAL_HOST=localhost:8536 262 | Environment=LEMMY_UI_LEMMY_EXTERNAL_HOST=example.com 263 | Environment=LEMMY_UI_HTTPS=true 264 | Restart=on-failure 265 | 266 | # Hardening 267 | ProtectSystem=full 268 | PrivateTmp=true 269 | NoNewPrivileges=true 270 | 271 | [Install] 272 | WantedBy=multi-user.target 273 | ``` 274 | 275 | More UI-related variables can be [found here](https://github.com/LemmyNet/lemmy-ui#configuration). 276 | 277 | Then run. 278 | 279 | ``` 280 | sudo systemctl daemon-reload 281 | sudo systemctl enable lemmy-ui 282 | sudo systemctl start lemmy-ui 283 | ``` 284 | 285 | If everything went right, the command `curl -I localhost:1234` should show `200 OK` at the top. 286 | 287 | ### Configure reverse proxy and TLS 288 | 289 | Install dependencies 290 | 291 | ```bash 292 | sudo apt install nginx certbot python3-certbot-nginx 293 | ``` 294 | 295 | Request Let's Encrypt TLS certificate (just follow the instructions) 296 | 297 | ```bash 298 | sudo certbot certonly --nginx 299 | ``` 300 | 301 | Let's Encrypt certificates should be renewed automatically, so add the line below to your crontab, by running `sudo crontab -e`. Replace `example.com` with your actual domain. 302 | 303 | ``` 304 | @daily certbot certonly --nginx --cert-name example.com -d example.com --deploy-hook 'nginx -s reload' 305 | ``` 306 | 307 | Finally, add the Nginx virtual host config file. Copy paste the file below to `/etc/nginx/sites-enabled/lemmy.conf` 308 | 309 | ``` 310 | limit_req_zone $binary_remote_addr zone={{domain}}_ratelimit:10m rate=1r/s; 311 | 312 | server { 313 | listen 80; 314 | listen [::]:80; 315 | server_name {{domain}}; 316 | location /.well-known/acme-challenge/ { 317 | root /var/www/certbot; 318 | } 319 | location / { 320 | return 301 https://$host$request_uri; 321 | } 322 | } 323 | 324 | server { 325 | listen 443 ssl http2; 326 | listen [::]:443 ssl http2; 327 | server_name {{domain}}; 328 | 329 | ssl_certificate /etc/letsencrypt/live/{{domain}}/fullchain.pem; 330 | ssl_certificate_key /etc/letsencrypt/live/{{domain}}/privkey.pem; 331 | 332 | # Various TLS hardening settings 333 | # https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html 334 | ssl_protocols TLSv1.2 TLSv1.3; 335 | ssl_prefer_server_ciphers on; 336 | 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:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; 337 | ssl_session_timeout 10m; 338 | ssl_session_cache shared:SSL:10m; 339 | ssl_session_tickets on; 340 | ssl_stapling on; 341 | ssl_stapling_verify on; 342 | 343 | # Hide nginx version 344 | server_tokens off; 345 | 346 | # Enable compression for JS/CSS/HTML bundle, for improved client load times. 347 | # It might be nice to compress JSON, but leaving that out to protect against potential 348 | # compression+encryption information leak attacks like BREACH. 349 | gzip on; 350 | gzip_types text/css application/javascript image/svg+xml; 351 | gzip_vary on; 352 | 353 | # Only connect to this site via HTTPS for the two years 354 | add_header Strict-Transport-Security "max-age=63072000"; 355 | 356 | # Various content security headers 357 | add_header Referrer-Policy "same-origin"; 358 | add_header X-Content-Type-Options "nosniff"; 359 | add_header X-Frame-Options "DENY"; 360 | add_header X-XSS-Protection "1; mode=block"; 361 | 362 | # Upload limit for pictrs 363 | client_max_body_size 20M; 364 | 365 | # frontend 366 | location / { 367 | # The default ports: 368 | 369 | set $proxpass "http://0.0.0.0:1234"; 370 | if ($http_accept ~ "^application/.*$") { 371 | set $proxpass "http://0.0.0.0:8536"; 372 | } 373 | if ($request_method = POST) { 374 | set $proxpass "http://0.0.0.0:8536"; 375 | } 376 | proxy_pass $proxpass; 377 | 378 | rewrite ^(.+)/+$ $1 permanent; 379 | 380 | # Send actual client IP upstream 381 | proxy_set_header X-Real-IP $remote_addr; 382 | proxy_set_header Host $host; 383 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 384 | } 385 | 386 | # backend 387 | location ~ ^/(api|pictrs|feeds|nodeinfo|.well-known) { 388 | proxy_pass http://0.0.0.0:8536; 389 | proxy_http_version 1.1; 390 | proxy_set_header Upgrade $http_upgrade; 391 | proxy_set_header Connection "upgrade"; 392 | 393 | # Rate limit 394 | limit_req zone={{domain}}_ratelimit burst=30 nodelay; 395 | 396 | # Add IP forwarding headers 397 | proxy_set_header X-Real-IP $remote_addr; 398 | proxy_set_header Host $host; 399 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 400 | } 401 | } 402 | 403 | access_log /var/log/nginx/access.log combined; 404 | ``` 405 | 406 | And then replace some variables in the file. Put your actual domain instead of example.com 407 | 408 | ``` 409 | sudo sed -i -e 's/{{domain}}/example.com/g' /etc/nginx/sites-enabled/lemmy.conf 410 | sudo systemctl reload nginx 411 | ``` 412 | 413 | Now open your Lemmy domain in the browser, and it should show you a configuration screen. Use it to create the first admin user and the default community. 414 | 415 | ## Upgrading 416 | 417 | ### Lemmy 418 | 419 | Compile and install lemmy_server changes. This compile can be done by a normal unprivledged user (using the same Linux account you used for rustup and first install of Lemmy). 420 | 421 | ``` 422 | rustup update 423 | cd lemmy 424 | git checkout main 425 | git pull --tags 426 | git checkout 0.18.5 # replace with version you are updating to 427 | git submodule update 428 | cargo build --release 429 | # copy compiled binary to destination 430 | # the old file will be locked by the already running application, so this sequence is recommended: 431 | sudo -- sh -c 'systemctl stop lemmy && cp target/release/lemmy_server /opt/lemmy/lemmy-server/lemmy_server && systemctl start lemmy' 432 | ``` 433 | 434 | ### Lemmy UI 435 | 436 | ``` 437 | cd /opt/lemmy/lemmy-ui 438 | sudo -u lemmy bash 439 | git checkout main 440 | git pull --tags 441 | git checkout 0.18.5 # replace with the version you are updating to 442 | git submodule update 443 | pnpm install 444 | pnpm build:prod 445 | exit 446 | sudo systemctl restart lemmy-ui 447 | ``` 448 | 449 | ### Pict-rs 450 | 451 | Refer to [pict-rs documentation](https://git.asonix.dog/asonix/pict-rs) for instructions on upgrading. 452 | -------------------------------------------------------------------------------- /src/contributors/05-federation.md: -------------------------------------------------------------------------------- 1 | # Federation 2 | 3 | Lemmy uses the ActivityPub protocol for communication between servers. If you are unfamiliar with the protocol, you can start by reading the [resource links](06-resources.md#activitypub-resources). This document explains how to interact with it from other projects. 4 | 5 | In Lemmy we use some specific terms to refer to ActivityPub items. They are essentially our specific implementations of well-known ActivityPub concepts: 6 | 7 | - Community: `Group` 8 | - User: `Person` 9 | - Post: `Page` 10 | - Comment: `Note` 11 | 12 | Almost every action in Lemmy happens inside a group. The Federation Enhancement Proposal [Group Federation](https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-1b12.md) gives a high-level overview how this works. The generic federation logic is implemented in the [activitypub-federation](https://github.com/LemmyNet/activitypub-federation-rust) library. It can also be used by other Rust projects. 13 | 14 | Sometimes you will see a notation like `Create/Note`. This refers to a `Create` activity with a `Note` as object. 15 | 16 | Below are explanations and examples for all actors, objects and activities from Lemmy. These include many optional fields which you can safely ignore. 17 | 18 | If you have trouble to make your project federate with Lemmy, feel free to [open an issue](https://github.com/LemmyNet/lemmy/issues). Make sure to include the specific federation messages you are sending, and any errors returned by Lemmy. 19 | 20 | ## Context 21 | 22 | ```json 23 | "@context": [ 24 | "https://join-lemmy.org/context.json", 25 | "https://www.w3.org/ns/activitystreams" 26 | ], 27 | ``` 28 | 29 | The context is identical for all activities and objects. 30 | 31 | ## Actors 32 | 33 | ### Community 34 | 35 | An automated actor. Users can send posts or comments to it, which the community forwards to its followers in the form of `Announce`. 36 | 37 | ```json 38 | {{#include ../../lemmy/crates/apub/assets/lemmy/objects/group.json}} 39 | ``` 40 | 41 | | Field Name | Description | 42 | | ------------------- | ----------------------------------------------------------------------------------------------------- | 43 | | `preferredUsername` | Name of the actor | 44 | | `name` | Title of the community | 45 | | `sensitive` | True indicates that all posts in the community are nsfw | 46 | | `attributedTo` | First the community creator, then all the remaining moderators | 47 | | `summary` | Text for the community sidebar, usually containing a description and rules | 48 | | `icon` | Icon, shown next to the community name | 49 | | `image` | Banner image, shown on top of the community page | 50 | | `inbox` | ActivityPub inbox URL | 51 | | `outbox` | ActivityPub outbox URL, only contains up to 20 latest posts, no comments, votes or other activities | 52 | | `followers` | Follower collection URL, only contains the number of followers, no references to individual followers | 53 | | `endpoints` | Contains URL of shared inbox | 54 | | `published` | Datetime when the community was first created | 55 | | `updated` | Datetime when the community was last changed | 56 | | `publicKey` | The public key used to verify signatures from this actor | 57 | 58 | ### User 59 | 60 | A person, interacts primarily with the community where it sends and receives posts/comments. Can also create and moderate communities, and send private messages to other users. Can be followed from other platforms. 61 | 62 | ```json 63 | {{#include ../../lemmy/crates/apub/assets/lemmy/objects/person.json}} 64 | ``` 65 | 66 | | Field Name | Description | 67 | | ------------------- | -------------------------------------------------------- | 68 | | `preferredUsername` | Name of the actor | 69 | | `name` | The user's displayname | 70 | | `summary` | User bio | 71 | | `icon` | The user's avatar, shown next to the username | 72 | | `image` | The user's banner, shown on top of the profile | 73 | | `inbox` | ActivityPub inbox URL | 74 | | `endpoints` | Contains URL of shared inbox | 75 | | `published` | Datetime when the user signed up | 76 | | `updated` | Datetime when the user profile was last changed | 77 | | `publicKey` | The public key used to verify signatures from this actor | 78 | 79 | ### Instance 80 | 81 | Represents a Lemmy instance, and is used to federate global data like the instance description or site bans. It can be fetched from the root path. 82 | 83 | ```json 84 | {{#include ../../lemmy/crates/apub/assets/lemmy/objects/instance.json}} 85 | ``` 86 | 87 | | Field Name | Description | 88 | | ----------- | -------------------------------------------------------- | 89 | | `name` | Instance name | 90 | | `summary` | Short description | 91 | | `content` | Long description (sidebar) | 92 | | `icon` | Instance icon | 93 | | `image` | Instance banner | 94 | | `inbox` | ActivityPub inbox URL | 95 | | `endpoints` | Contains URL of shared inbox | 96 | | `published` | Datetime when the instance was created | 97 | | `updated` | Datetime when the instance metadata | 98 | | `publicKey` | The public key used to verify signatures from this actor | 99 | 100 | ## Objects 101 | 102 | ### Post 103 | 104 | A page with title, and optional URL and text content. The attachment URL often leads to an image, in which case a thumbnail is included. Each post belongs to exactly one community. Sent out as `Page`, but for receiving the types `Article`, `Note`, `Video` and `Event` are also accepted. 105 | 106 | ```json 107 | {{#include ../../lemmy/crates/apub/assets/lemmy/objects/page.json}} 108 | ``` 109 | 110 | | Field Name | Description | 111 | | ----------------- | --------------------------------------------------------------------------------------------------- | 112 | | `attributedTo` | ID of the user which created this post | 113 | | `to` | ID of the community where it was posted to | 114 | | `name` | Title of the post (mandatory) | 115 | | `content` | Body of the post | 116 | | `attachment` | A single website or image link | 117 | | `image` | Thumbnail for `url`, only present if it is an image link | 118 | | `commentsEnabled` | False indicates that the post is locked, and no comments can be added | 119 | | `sensitive` | True marks the post as NSFW, blurs the thumbnail and hides it from users with NSFW setting disabled | 120 | | `stickied` | True means that it is shown on top of the community | 121 | | `published` | Datetime when the post was created | 122 | | `updated` | Datetime when the post was edited (not present if it was never edited) | 123 | 124 | ### Comment 125 | 126 | A reply to a post, or reply to another comment. Contains only text (including references to other users or communities). Lemmy displays comments in a tree structure. 127 | 128 | ```json 129 | {{#include ../../lemmy/crates/apub/assets/lemmy/objects/note.json}} 130 | ``` 131 | 132 | | Field Name | Description | 133 | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------- | 134 | | `attributedTo` | ID of the user who created the comment | 135 | | `to` | Community where the comment was made | 136 | | `content` | The comment text | 137 | | `inReplyTo` | ID of the parent object. In case of a top-level comment this is the post ID, in case of a nested comment it is the parent comment ID. | 138 | | `published` | Datetime when the comment was created | 139 | | `updated` | Datetime when the comment was edited (not present if it was never edited) | 140 | 141 | ### Private Message 142 | 143 | A direct message from one user to another. Can not include additional users. Threading is not implemented yet, so the `inReplyTo` field is missing. 144 | 145 | ```json 146 | {{#include ../../lemmy/crates/apub/assets/lemmy/objects/chat_message.json}} 147 | ``` 148 | 149 | | Field Name | Description | 150 | | -------------- | ------------------------------------------------------------------------- | 151 | | `attributedTo` | ID of the user who created this private message | 152 | | `to` | ID of the recipient | 153 | | `content` | The text of the private message | 154 | | `published` | Datetime when the message was created | 155 | | `updated` | Datetime when the message was edited (not present if it was never edited) | 156 | 157 | ## Collections 158 | 159 | ### Community Outbox 160 | 161 | ```json 162 | {{#include ../../lemmy/crates/apub/assets/lemmy/collections/group_outbox.json}} 163 | ``` 164 | 165 | The outbox only contains `Create/Post` activities for now. 166 | 167 | ### Community Followers 168 | 169 | ```json 170 | {{#include ../../lemmy/crates/apub/assets/lemmy/collections/group_followers.json}} 171 | ``` 172 | 173 | The followers collection is only used to expose the number of followers. Actor IDs are not included, to protect user privacy. 174 | 175 | ### Community Moderators 176 | 177 | List of moderators who can perform actions like removing posts or banning users. 178 | 179 | ```json 180 | {{#include ../../lemmy/crates/apub/assets/lemmy/collections/group_moderators.json}} 181 | ``` 182 | 183 | ### Community Featured Posts 184 | 185 | List of posts which are stickied in the community. 186 | 187 | ```json 188 | {{#include ../../lemmy/crates/apub/assets/lemmy/collections/group_featured_posts.json}} 189 | ``` 190 | 191 | ### User Outbox 192 | 193 | Only contains `totalItems` count, but no actual `items` for privacy reasons. 194 | 195 | ```json 196 | {{#include ../../lemmy/crates/apub/assets/lemmy/collections/person_outbox.json}} 197 | ``` 198 | 199 | ## Activities 200 | 201 | ### User to Community 202 | 203 | #### Follow 204 | 205 | Each Community page has a "Follow" button. Clicking this triggers a `Follow` activity to be sent from the user to the Community inbox. The Community will automatically respond with an `Accept/Follow` activity to the user inbox. It will also add the user to its list of followers, and deliver any activities about Posts/Comments in the Community to the user. 206 | 207 | ```json 208 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/following/follow.json}} 209 | ``` 210 | 211 | #### Unfollow 212 | 213 | After following a Community, the "Follow" button is replaced by "Unfollow". Clicking this sends an `Undo/Follow` activity to the Community inbox. The Community removes the User from its followers list and doesn't send any activities to it anymore. 214 | 215 | ```json 216 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/following/undo_follow.json}} 217 | ``` 218 | 219 | #### Create or Update Post 220 | 221 | When a user creates a new post, it is sent to the respective community as `Create/Page`. Editing a previously created post sends an almost identical activity, except the `type` being `Update`. 222 | 223 | ```json 224 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/create_or_update/create_page.json}} 225 | ``` 226 | 227 | #### Create or Update Comment 228 | 229 | A reply to a post, or to another comment as `Create/Note`. Can contain mentions of other users. Editing a previously created post sends an almost identical activity, except the `type` being `Update`. 230 | 231 | The origin instance also scans the Comment for any User mentions, and sends the `Create/Note` to 232 | those Users as well. 233 | 234 | ```json 235 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/create_or_update/create_note.json}} 236 | ``` 237 | 238 | #### Like Post or Comment 239 | 240 | An upvote for a post or comment. 241 | 242 | ```json 243 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/voting/like_note.json}} 244 | ``` 245 | 246 | #### Dislike Post or Comment 247 | 248 | A downvote for a post or comment. 249 | 250 | ```json 251 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/voting/dislike_page.json}} 252 | ``` 253 | 254 | #### Undo Like or Dislike Post or Comment 255 | 256 | Revert a vote that was previously done by the same user. 257 | 258 | ```json 259 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/voting/undo_like_note.json}} 260 | ``` 261 | 262 | #### Delete Post or Comment 263 | 264 | Mods can remove Posts and Comments from their Communities. Admins can remove any Posts or Comments on the entire site. Communities can also be removed by admins. The item is then hidden from all users. 265 | 266 | Removals are sent to all followers of the Community, so that they also take effect there. The exception is if an admin removes an item from a Community which is hosted on a different instance. In this case, the removal only takes effect locally. 267 | 268 | ```json 269 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/delete_page.json}} 270 | ``` 271 | 272 | #### Undo Delete 273 | 274 | Post or comment deletions can be reverted by the same user. 275 | 276 | ```json 277 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json}} 278 | ``` 279 | 280 | #### Report Post, comment or private message 281 | 282 | Reports content for rule violation, so that mods/admins can review it. 283 | 284 | ```json 285 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/report_page.json}} 286 | ``` 287 | 288 | #### Delete User 289 | 290 | Sent when a user deletes his own account. 291 | 292 | ```json 293 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/delete_user.json}} 294 | ``` 295 | 296 | ### Community to User 297 | 298 | #### Accept Follow 299 | 300 | Automatically sent by the community in response to a `Follow`. At the same time, the community adds this user to its followers list. 301 | 302 | ```json 303 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/following/accept.json}} 304 | ``` 305 | 306 | #### Announce 307 | 308 | If the Community receives any Post or Comment related activity (Create, Update, Like, Dislike, Remove, Delete, Undo etc.), it will forward this to its followers. For this, an Announce is created with the Community as actor, and the received activity as object. This is sent to all followers, so they get updated in real time. 309 | 310 | ```json 311 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/announce_create_page.json}} 312 | ``` 313 | 314 | ### Moderation 315 | 316 | These actions can only be done by instance admins or community moderators. They are sent to the community and announced by it. See [](../users/04-moderation.md) for a general overview how moderation works in Lemmy. Communities can only be created on the same instance where a user is registered. After that, mods from other instances can be added with `Add/User` activity. 317 | 318 | #### Remove Post or Comment 319 | 320 | Removes a post or comment. The difference to delete is that remove activities have a summary field, which contains the reason for removal, as provided by the mod/admin. 321 | 322 | ```json 323 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/remove_note.json}} 324 | ``` 325 | 326 | #### Block User 327 | 328 | Blocks a user so he can't participate anymore. The scope is determined by the `target` field: either a community, or a whole instance. The `removeData` field can optionally be set to indicate that all previous posts of the user should be deleted. 329 | 330 | ```json 331 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/block/block_user.json}} 332 | ``` 333 | 334 | #### Lock post 335 | 336 | Posts can be locked so that no new comments can be created. 337 | 338 | ```json 339 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/lock_page.json}} 340 | ``` 341 | 342 | #### Undo mod actions 343 | 344 | All previously listed mod actions can be reverted by wrapping the original activity in `Undo`. Note that Lemmy regenerates the inner activity with a new ID. 345 | 346 | ```json 347 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json}} 348 | ``` 349 | 350 | ```json 351 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/block/undo_block_user.json}} 352 | ``` 353 | 354 | #### Add or remove featured post 355 | 356 | Posts can be pinned so that they are always shown on top of the community. This is federated with the [Community featured posts](#community-featured-posts) collection. 357 | 358 | ```json 359 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/add_featured_post.json}} 360 | ``` 361 | 362 | ```json 363 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/remove_featured_post.json}} 364 | ``` 365 | 366 | #### Add or remove mod 367 | 368 | Add a new mod to the community. Has to be sent by an existing community mod, or an admin of the community's instance. 369 | 370 | ```json 371 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/add_mod.json}} 372 | ``` 373 | 374 | An existing mod can be removed in the same way. 375 | 376 | ```json 377 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/community/remove_mod.json}} 378 | ``` 379 | 380 | ### User to User 381 | 382 | #### Follow a user 383 | 384 | Users from other platforms can follow Lemmy users and receive all of their posts to the inbox. Note that users who are registered on Lemmy can only follow groups, not other users. 385 | 386 | ```json 387 | {{#include ../../lemmy/crates/apub/assets/pleroma/activities/follow.json}} 388 | ``` 389 | 390 | #### Create or Update Private message 391 | 392 | User profiles have a "Send Message" button, which opens a dialog permitting to send a private message to this user. It is sent as a `Create/ChatMessage` to the user inbox. Private messages can only be directed at a single User. They can also be edited with `Update/ChatMessage`. 393 | 394 | ```json 395 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/create_or_update/create_private_message.json}} 396 | ``` 397 | 398 | #### Delete Private Message 399 | 400 | Deletes a previous private message. 401 | 402 | ```json 403 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/delete_private_message.json}} 404 | ``` 405 | 406 | #### Undo Delete Private Message 407 | 408 | Restores a previously deleted private message. The `object` is regenerated from scratch, as such the activity ID and other fields are different. 409 | 410 | ```json 411 | {{#include ../../lemmy/crates/apub/assets/lemmy/activities/deletion/undo_delete_private_message.json}} 412 | ``` 413 | --------------------------------------------------------------------------------