Rewrite contributing section (#183)

This commit is contained in:
Nutomic 2023-06-02 14:43:31 +02:00 committed by GitHub
parent ef6ce5d945
commit 98eb003eb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 462 additions and 771 deletions

View file

@ -27,22 +27,14 @@
- [Backup and Restore](administration/backup_and_restore.md)
- [Using Caddy as a reverse proxy](administration/caddy.md)
# For Developers
# For Contributors
- [Federation](federation/federation.md)
- [Federation Overview](federation/overview.md)
- [Lemmy Protocol](federation/lemmy_protocol.md)
- [Client Development](client_development/client_development.md)
- [HTTP API extras](client_development/http_api_extras.md)
- [Rust API](client_development/rust_api.md)
- [Creating a Custom Frontend](client_development/custom_frontend.md)
- [Contributing](contributing/contributing.md)
- [Docker Development](contributing/docker_development.md)
- [Local Development](contributing/local_development.md)
- [Tests](contributing/tests.md)
- [Federation Development](contributing/federation_development.md)
- [Release Process](contributing/release_process.md)
- [Resources](contributing/resources.md)
- [Overview](contributors/01-overview.md)
- [Local Development](contributors/02-local-development.md)
- [Docker Development](contributors/03-docker-development.md)
- [API](contributors/04-api.md)
- [Federation](contributors/05-federation.md)
- [Resources](contributors/06-resources.md)
# Code of Conduct

View file

@ -1,4 +0,0 @@
# Client Development
- [API docs](/api)
- [HTTP API extras](http_api_extras.md)

View file

@ -1,66 +0,0 @@
# Creating a Custom Frontend
The backend and frontend are completely decoupled, and run in independent Docker containers. They only communicate over the [Lemmy API](client_development.md), which makes it quite easy to write alternative frontends.
This creates a lot of potential for custom frontends, which could change much of the design and user experience of Lemmy. For example, it would be possible to create a frontend in the style of a traditional forum like [phpBB](https://www.phpbb.com/), or a question-and-answer site like [stackoverflow](https://stackoverflow.com/). All without having to think about database queries, authentification or ActivityPub, which you essentially get for free.
## Development
You can use any language to create a custom frontend. The easiest option would be forking our [official frontend](https://github.com/LemmyNet/lemmy-ui), [lemmy-lite](https://github.com/IronOxidizer/lemmy-lite), or the [lemmy-frontend-example](https://github.com/LemmyNet/lemmy-front-end-example). In any case, the principle is the same: bind to `LEMMY_EXTERNAL_HOST` (default: `localhost:8536`) and handle requests using the Lemmy API at `LEMMY_INTERNAL_HOST` (default: `lemmy:8536`). Also use `LEMMY_HTTPS` to generate links with the correct protocol.
The next step is building a Docker image from your frontend. If you forked an existing project, it should already include a `Dockerfile` and instructions to build it. Otherwise, try searching for your language on [dockerhub](https://hub.docker.com/), official images usually have build instructions in their readme. Build a Docker image with a tag, then look for the following section in `docker/dev/docker-compose.yml`:
```
lemmy-ui:
image: dessalines/lemmy-ui:v0.8.10
ports:
- "1235:1234"
restart: always
environment:
- LEMMY_INTERNAL_HOST=lemmy:8536
- LEMMY_EXTERNAL_HOST=localhost:8536
- LEMMY_HTTPS=false
depends_on:
- lemmy
```
All you need to do is replace the value for `image` with the tag of your own Docker image (and possibly the environment variables if you need different ones). Then run `./docker_update.sh`, and after compilation, your frontend will be available on `http://localhost:1235`. You can also make the same change to `docker/federation/docker-compose.yml` and run `./start-local-instances.bash` to test federation with your frontend.
## Deploy with Docker
After building the Docker image, you need to push it to a Docker registry (such as [dockerhub](https://hub.docker.com/)). Then update the `docker-compose.yml` on your server, replacing the `image` for `lemmy-ui`, just as described above. Run `docker-compose.yml`, and after a short wait, your instance will use the new frontend.
Note, if your instance is deployed with Ansible, it will override `docker-compose.yml` with every run, reverting back to the default frontend. In that case you should copy the `ansible/` folder from this project to your own repository, and adjust `docker-compose.yml` directly in the repo.
It is also possible to use multiple frontends for the same Lemmy instance, either using subdomains or subfolders. To do that, don't edit the `lemmy-ui` section in `docker-compose.yml`, but duplicate it, adjusting the name, image and port so they are distinct for each. Then edit your nginx config to pass requests to the appropriate frontend, depending on the subdomain or path.
## Translations
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.
## Rate limiting
Lemmy does rate limiting for many actions based on the client IP. But if you make any API calls on the server side (eg 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 headers `X-REAL-IP` and `X-FORWARDED-FOR` on to Lemmy (the headers are set by our nginx config).
Here is an example snipped for NodeJS:
```javascript
function setForwardedHeaders(headers: IncomingHttpHeaders): {
[key: string]: string,
} {
let out = {
host: headers.host,
};
if (headers["x-real-ip"]) {
out["x-real-ip"] = headers["x-real-ip"];
}
if (headers["x-forwarded-for"]) {
out["x-forwarded-for"] = headers["x-forwarded-for"];
}
return out;
}
let headers = setForwardedHeaders(req.headers);
let client = new LemmyHttp(httpUri, headers);
```

View file

@ -1,84 +0,0 @@
# Lemmy HTTP API Extras
This contains extras not in the [API docs](/api).
<!-- toc -->
- [Curl Examples](#curl-examples)
- [HTTP API exclusive features](#http-api-exclusive-features)
- [RSS/Atom feeds](#rssatom-feeds)
- [Images](#images)
- [Create (request)](#create-request)
- [Create (response)](#create-response)
- [Delete](#delete)
<!-- tocstop -->
## Curl Examples
**GET example**
The current api `{version}` is [here](https://github.com/LemmyNet/lemmy-js-client/blob/main/src/types/others.ts#L1).
```
curl "http://localhost:8536/api/{version}/community/list?sort=Hot"`
```
**POST example**
```
curl -i -H \
"Content-Type: application/json" \
-X POST \
-d '{
"comment_id": 374,
"score": 1,
"auth": eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiaXNzIjoidGVzdC5sZW1teS5tbCJ9.P77RX_kpz1a_geY5eCp29sl_5mAm-k27Cwnk8JcIZJk
}' \
http://localhost:8536/api/{version}/comment/like
```
## HTTP API exclusive features
These features cannot be accessed from the WebSocket API:
- [RSS/Atom feeds](#rss-atom-feeds)
- [Images](#images)
### RSS/Atom feeds
- All - `/feeds/all.xml?sort=Hot`
- Community - `/feeds/c/community-name.xml?sort=Hot`
- User - `/feeds/u/user-name.xml?sort=Hot`
### Images
Lemmy forwards image requests to a locally running Pictrs.
`GET /pictrs/image/{filename}?format={webp, jpg, ...}&thumbnail={96}`
_Format and thumbnail are optional._
#### Create (request)
Uploaded content must be valid multipart/form-data with an image array located within the images[] key.
`POST /pictrs/image`
#### Create (response)
```
{
"files": [
{
"delete_token": "{token}",
"file": "{file}.jpg"
}
],
"msg": "ok"
}
```
#### Delete
`GET /pictrs/image/delete/{delete_token}/{file}`

View file

@ -1,37 +0,0 @@
# Rust API
If you want to develop a Rust application which interacts with Lemmy, you can directly pull in the relevant API structs. This relies on the exact code used in Lemmy, but with most heavyweight dependencies disabled (like diesel).
To get started, add the following to your `Cargo.toml`:
```
[dependencies]
lemmy_api_common = { git = "https://github.com/LemmyNet/lemmy.git" }
```
Note, at the time of writing, this code is not available on crates.io yet. You can use "0.16.3" from crates.io, but it pulls in many heavy dependencies including diesel. Best use the git dependency for now, or wait for a newer version to become available.
You can then use the following code to make an API request:
```rust
use lemmy_api_common::post::{GetPosts, GetPostsResponse};
use lemmy_db_schema::{ListingType, SortType};
use ureq::Agent;
pub fn list_posts() -> GetPostsResponse {
let params = GetPosts {
type_: Some(ListingType::Local),
sort: Some(SortType::New),
..Default::default()
};
Agent::new()
.get("https://lemmy.ml/post/list")
.send_json(&params).unwrap()
.into_json().unwrap()
}
```
You can also look at the following real-world projects as examples:
- [lemmyBB](https://github.com/LemmyNet/lemmyBB)
- [lemmy-stats-crawler](https://github.com/LemmyNet/lemmy-stats-crawler)

View file

@ -1,39 +0,0 @@
# Contributing
Information about contributing to Lemmy, whether it is translating, testing, designing or programming.
## Issue tracking / Repositories
- [GitHub (for issues and pull requests)](https://github.com/LemmyNet/lemmy)
- [Gitea (only for pull requests)](https://git.join-lemmy.org/LemmyNet/lemmy/)
- [Codeberg](https://codeberg.org/LemmyNet/lemmy)
## Translating
Check out [Lemmy's Weblate](https://weblate.join-lemmy.org/projects/lemmy/) for translations. You can also help by [translating this documentation](https://github.com/LemmyNet/lemmy-docs#adding-a-new-language).
## Architecture
### Front end
- The front end is written in `typescript`, using a react-like framework called [inferno](https://infernojs.org/). All UI elements are reusable `.tsx` components.
- The front end repository is [lemmy-ui](https://github.com/LemmyNet/lemmy-ui).
- The routes are at `src/shared/routes.ts`.
- The components are located in `src/shared/components`.
### Back end
- The back end is written in `rust`, using `diesel`, and `actix`.
- The server source code is split into main sections in `src`. These include:
- `db` - The low level database actions.
- Database additions are done using diesel migrations. Run `diesel migration generate xxxxx` to add new things.
- `api` - The high level user interactions (things like `CreateComment`)
- `routes` - The server endpoints .
- `apub` - The activitypub conversions.
- `websocket` - Creates the websocket server.
## Linting / Formatting
- Every front and back end commit is automatically formatted then linted using `husky`, and `lint-staged`.
- Rust with `cargo fmt` and `cargo clippy`.
- Typescript with `prettier` and `eslint`.

View file

@ -1,42 +0,0 @@
# Docker Development
## Dependencies
### Debian-based distro
```bash
sudo apt install git docker-compose
sudo systemctl start docker
git clone https://github.com/LemmyNet/lemmy --recursive
```
### Arch-based distro
```bash
sudo pacman -S git docker-compose
sudo systemctl start docker
git clone https://github.com/LemmyNet/lemmy --recursive
```
## Running
```bash
cd docker/dev
./docker_update.sh
```
and go to http://localhost:1236
\*Note: many features (like docs and pictures) will not work without using an nginx profile like that in `ansible/templates/nginx.conf`.
To speed up the Docker compile, add the following to `/etc/docker/daemon.json` and restart Docker.
```
{
"features": {
"buildkit": true
}
}
```
If the build is still too slow, you will have to use a [local build](local_development.md) instead.

View file

@ -1,61 +0,0 @@
# Federation Development
## Running locally
Install the dependencies as described in [Docker development](docker_development.md). Then run the following
```bash
cd docker/federation
./start-local-instances.bash
```
The federation test sets up 5 instances:
| Instance | Username | Location | Notes |
| ------------- | ------------- | --------------------------------------- | --------------------------------------- |
| lemmy-alpha | lemmy_alpha | [127.0.0.1:8540](http://127.0.0.1:8540) | federated with all other instances |
| lemmy-beta | lemmy_beta | [127.0.0.1:8550](http://127.0.0.1:8550) | federated with all other instances |
| lemmy-gamma | lemmy_gamma | [127.0.0.1:8560](http://127.0.0.1:8560) | federated with all other instances |
| lemmy-delta | lemmy_delta | [127.0.0.1:8570](http://127.0.0.1:8570) | only allows federation with lemmy-beta |
| lemmy-epsilon | lemmy_epsilon | [127.0.0.1:8580](http://127.0.0.1:8580) | uses blocklist, has lemmy-alpha blocked |
You can log into each using the instance name, and `lemmy` as the password, IE (`lemmy_alpha`, `lemmy`).
To start federation between instances, visit one of them and search for a user, community or post, like this. Note that
the Lemmy backend runs on a different port than the frontend, so you have to increment the port number from
the URL bar by one.
- `!main@lemmy-alpha:8541`
- `http://lemmy-beta:8551/post/3`
- `@lemmy-gamma@lemmy-gamma:8561`
Firefox containers are a good way to test them interacting.
## Running on a server
Note that federation is currently in alpha. **Only use it for testing**, not on any production server, and be aware that turning on federation may break your instance.
Follow the normal installation instructions, either with [Ansible](../administration/install_ansible.md) or
[manually](../administration/install_docker.md). Then replace the line `image: dessalines/lemmy:v0.x.x` in
`/lemmy/docker-compose.yml` with `image: dessalines/lemmy:federation`.
Afterwards, and whenever you want to update to the latest version, run these commands on the server:
```
cd /lemmy/
sudo docker-compose pull
sudo docker-compose up -d
```
## Security Model
- HTTP signature verify: This ensures that activity really comes from the activity that it claims
- check_is_apub_valid : Makes sure its in our allowed instances list
- Lower level checks: To make sure that the user that creates/updates/removes a post is actually on the same instance as that post
For the last point, note that we are _not_ checking whether the actor that sends the create activity for a post is
actually identical to the post's creator, or that the user that removes a post is a mod/admin. These things are checked
by the API code, and its the responsibility of each instance to check user permissions. This does not leave any attack
vector, as a normal instance user cant do actions that violate the API rules. The only one who could do that is the
admin (and the software deployed by the admin). But the admin can do anything on the instance, including send activities
from other user accounts. So we wouldnt actually gain any security by checking mod permissions or similar.

View file

@ -1,115 +0,0 @@
# Local Development
### Install build requirements
Install Rust using [the recommended option on rust-lang.org](https://www.rust-lang.org/tools/install) (rustup).
#### Debian-based distro
```
sudo apt install git cargo libssl-dev pkg-config libpq-dev curl
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
```
#### Arch-based distro
```
sudo pacman -S git cargo libssl-dev pkg-config libpq-dev curl
# install yarn (stable)
curl -o- -L https://yarnpkg.com/install.sh | bash
```
#### macOS
Install [Homebrew](https://brew.sh/) if you don't already have it installed.
Finally, install Node and Yarn.
```
brew install node yarn
```
### Get the back end source code
```
git clone https://github.com/LemmyNet/lemmy.git --recursive
# or alternatively from gitea
# git clone https://yerbamate.ml/LemmyNet/lemmy.git --recursive
```
### Build the backend (Rust)
```
cargo build
# for development, use `cargo check` instead)
```
### Get the front end source code
```
git clone https://github.com/LemmyNet/lemmy-ui.git --recursive
```
### Setup postgresql
#### Debian-based disto
```
sudo apt install postgresql
sudo systemctl start postgresql
# Either execute db-init.sh, or manually initialize the postgres database:
sudo -u postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
sudo -u postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
```
#### Arch-based distro
```
sudo pacman -S postgresql
sudo systemctl start postgresql
# Either execute db-init.sh, or manually initialize the postgres database:
sudo -u postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
sudo -u postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
```
#### macOS
```
brew install postgresql
brew services start postgresql
/usr/local/opt/postgres/bin/createuser -s postgres
# Either execute db-init.sh, or manually initialize the postgres database:
psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
```
### Run a local development instance
```
cd lemmy
cargo run
```
Then open [localhost:1235](http://localhost:1235) in your browser. To reload back-end changes, you will have to rerun `cargo run`. You can use `cargo check` as a faster way to find compilation errors.
To do front end development:
```
cd lemmy-ui
yarn
yarn dev
```
and go to [localhost:1234](http://localhost:1234). Front end saves should rebuild the project.
Note that this setup doesn't include image uploads. If you want to test those, you should use the
[Docker development](docker_development.md).

View file

@ -1,19 +0,0 @@
# Branching and Releases
## Branches
In general, our handling of branches is the one described in [A stable mainline branching model for Git](https://www.bitsnbites.eu/a-stable-mainline-branching-model-for-git/). One difference is that we avoid rebase, and instead merge the base branch into the current working branch. This helps to avoid force pushes and conflicts.
## Releases
- For major release: make a new branch `release/v0.x`
- For minor release: cherry-pick desired changes onto `release/v0.x` branch
- Make a beta or release candidate version with `docker/prod/deploy.sh`
- Do the same for `lemmy-ui`: `./deploy.sh 0.x.0-rc-x`
- Deploy to federation test instances
- Keeping one instance at the last stable version to test federation compatibility (automate this with ansible)
- `ansible-playbook -i federation playbooks/site.yml --vault-password-file vault_pass -e rc_version=0.x.0-rc.x`
- Test that everything works as expected, make new beta/rc releases if needed
- Deploy to lemmy.ml, to discover remaining problems
- If that went well, make the official `0.x.0` release with `docker/prod/deploy.sh`
- Announce the release on Lemmy, Matrix, Mastodon

View file

@ -1,30 +0,0 @@
### Tests
#### Rust
After installing [local development dependencies](local_development.md), run the
following commands:
```bash
psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
./test.sh
```
### Federation
Install the [Local development dependencies](local_development.md), and add the following lines to `/etc/hosts`:
```
127.0.0.1 lemmy-alpha
127.0.0.1 lemmy-beta
127.0.0.1 lemmy-gamma
127.0.0.1 lemmy-delta
127.0.0.1 lemmy-epsilon
```
Then use the following script to run the tests:
```
cd api_tests
./run-federation-test.bash
```

View file

@ -0,0 +1,30 @@
## Contributing
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).
These are the main repositories which are relevant for contributors:
- [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.
- [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.
- [lemmy-ansible](https://github.com/LemmyNet/lemmy-ansible): Automated installation method which is recommended for users without technical knowledge.
- [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.
- [lemmy-js-client](https://github.com/LemmyNet): Client for the Lemmy API which is used by lemmy-ui. Can also be used by other projects to get started more easily.
- [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.
There are many different ways to contribute, depending on your skills and interests:
### Reporting issues
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.
### Translating
Use [Weblate](https://weblate.join-lemmy.org/projects/lemmy/) to help translate Lemmy into different languages.
### Programming and Design
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.
### Donating
Besides contributing with your time, you can also consider [making a donation](https://join-lemmy.org/donate) to support the developers.

View file

@ -0,0 +1,110 @@
## Local Development
### Install build requirements
Install the latest Rust version using [rustup](https://www.rust-lang.org/tools/install).
Debian-based distro:
```bash
sudo apt install git cargo libssl-dev pkg-config libpq-dev curl
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
```
Arch-based distro:
```bash
sudo pacman -S git cargo libssl-dev pkg-config libpq-dev curl
# install yarn (stable)
curl -o- -L https://yarnpkg.com/install.sh | bash
```
macOS:
Install [Homebrew](https://brew.sh/) if you don't already have it installed. Then install Node and Yarn.
```bash
brew install node yarn
```
### Setup PostgreSQL database
Debian-based disto:
```bash
sudo apt install postgresql
sudo systemctl start postgresql
```
Arch-based distro:
```bash
sudo pacman -S postgresql
sudo systemctl start postgresql
```
macOS:
```bash
brew install postgresql
brew services start postgresql
/usr/local/opt/postgres/bin/createuser -s postgres
```
Either execute `scripts/db-init.sh`, or manually initialize the postgres database:
```bash
psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
```
### Get the code
Clone frontend and backend code to local folders. Be sure to include `--recursive` to initialize git submodules.
```bash
git clone https://github.com/LemmyNet/lemmy.git --recursive
git clone https://github.com/LemmyNet/lemmy-ui.git --recursive
```
### Backend development
Use `cargo check` to find compilation errors. To start the Lemmy backend, run `cargo run`. It will bind to `0.0.0.0:8536`.
After making changes, you need to format the code with `cargo +nightly fmt --all` and run the linter with `./scripts/fix-clippy.sh`.
### Frontend development
Install dependencies by running `yarn`. Then run `yarn start` 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.
Note that this setup doesn't support image uploads. If you want to test those, you need to use the
[Docker development](03-docker-development.md).
### Tests
Run Rust unit tests:
```bash
./scripts/test.sh
```
To run federation/API tests, first add the follwing lines to `/etc/hosts`:
```
127.0.0.1 lemmy-alpha
127.0.0.1 lemmy-beta
127.0.0.1 lemmy-gamma
127.0.0.1 lemmy-delta
127.0.0.1 lemmy-epsilon
```
Then run the following script:
```bash
cd api_tests
./run-federation-test.bash
```

View file

@ -0,0 +1,64 @@
## Docker Development
### Dependencies
Debian-based distro:
```bash
sudo apt install git docker-compose
sudo systemctl start docker
git clone https://github.com/LemmyNet/lemmy --recursive
```
Arch-based distro:
```bash
sudo pacman -S git docker-compose
sudo systemctl start docker
```
Get the code with submodules:
```bash
git clone https://github.com/LemmyNet/lemmy --recursive
```
### Running
```bash
cd docker
./docker_update.sh
```
Then open [localhost:1236](http://localhost:1236) in your browser.
Building with Docker is relatively slow. To get faster builds you need to use [local development](02-local-development.md) instead.
## Federation Development
The federation setup allows starting a few local Lemmy instances, to test federation functionality. You can start it with the following commands:
```bash
cd docker/federation
./start-local-instances.bash
```
The federation test sets up 5 instances:
| Instance | Admin username | Location | Notes |
| ------------- | -------------- | --------------------------------------- | --------------------------------------- |
| lemmy-alpha | lemmy_alpha | [127.0.0.1:8540](http://127.0.0.1:8540) | federated with all other instances |
| lemmy-beta | lemmy_beta | [127.0.0.1:8550](http://127.0.0.1:8550) | federated with all other instances |
| lemmy-gamma | lemmy_gamma | [127.0.0.1:8560](http://127.0.0.1:8560) | federated with all other instances |
| lemmy-delta | lemmy_delta | [127.0.0.1:8570](http://127.0.0.1:8570) | only allows federation with lemmy-beta |
| lemmy-epsilon | lemmy_epsilon | [127.0.0.1:8580](http://127.0.0.1:8580) | uses blocklist, has lemmy-alpha blocked |
You can log into each using the instance name, and `lemmy` as the password, IE (`lemmy_alpha`, `lemmy`).
To start federation between instances, visit one of them and search for a user, community or post, like this. Note that
the Lemmy backend runs on a different port than the frontend, so you have to increment the port number from
the URL bar by one.
- `http://lemmy-gamma:8561/u/lemmy-gamma`
- `http://lemmy-alpha:8541/c/main`
- `http://lemmy-beta:8551/post/3`

View file

@ -0,0 +1,112 @@
## API
Lemmy has an HTTP API for clients and frontends. See the [API documentation](/api) for more information. 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:
- https://enterprise.lemmy.ml/
- https://ds9.lemmy.ml/
- https://voyager.lemmy.ml/
### Curl Examples
**GET example**
The current api `{version}` is [here](https://github.com/LemmyNet/lemmy-js-client/blob/main/src/types/others.ts#L1).
```
curl "https://lemmy.ml/api/{version}/community/list?sort=Hot"`
```
**POST example**
```
curl -i -H \
"Content-Type: application/json" \
-X POST \
-d '{
"comment_id": 374,
"score": 1,
"auth": eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiaXNzIjoidGVzdC5sZW1teS5tbCJ9.P77RX_kpz1a_geY5eCp29sl_5mAm-k27Cwnk8JcIZJk
}' \
https://lemmy.ml/api/{version}/comment/like
```
### RSS/Atom feeds
- All - `/feeds/all.xml?sort=Hot`
- Community - `/feeds/c/community-name.xml?sort=Hot`
- User - `/feeds/u/user-name.xml?sort=Hot`
### Images
Lemmy forwards image requests to a locally running Pictrs.
`GET /pictrs/image/{filename}?format={webp, jpg, ...}&thumbnail={96}`
_Format and thumbnail are optional._
#### Create (request)
Uploaded content must be valid multipart/form-data with an image array located within the images[] key.
`POST /pictrs/image`
#### Create (response)
```
{
"files": [
{
"delete_token": "{token}",
"file": "{file}.jpg"
}
],
"msg": "ok"
}
```
#### Delete
`GET /pictrs/image/delete/{delete_token}/{file}`
### Rust API
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. To get started, run `cargo add lemmy_api_common` in your repository. You can then use the following code to make an API request:
```rust
use lemmy_api_common::post::{GetPosts, GetPostsResponse};
use lemmy_db_schema::{ListingType, SortType};
use ureq::Agent;
pub fn list_posts() -> GetPostsResponse {
let params = GetPosts {
type_: Some(ListingType::Local),
sort: Some(SortType::New),
..Default::default()
};
Agent::new()
.get("https://lemmy.ml/api/v3/post/list")
.send_json(&params).unwrap()
.into_json().unwrap()
}
```
You can also look at the following real-world projects as examples:
- [lemmyBB](https://github.com/LemmyNet/lemmyBB)
- [lemmy-stats-crawler](https://github.com/LemmyNet/lemmy-stats-crawler)
### Creating a Custom Frontend
The Lemmy backend and frontend are completely seperate 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 [stackoverflow](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.
#### Development
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.
#### Translations
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.
#### Rate limiting
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.

View file

@ -1,54 +1,19 @@
# Lemmy Federation Protocol
## Federation
The Lemmy Protocol (or Lemmy Federation Protocol) is a subset of the [ActivityPub Protocol](https://www.w3.org/TR/activitypub/), with some extensions.
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.
This document is targeted at developers who are familiar with the ActivityPub and ActivityStreams protocols. It gives a detailed outline of the actors, objects and activities used by Lemmy.
In Lemmy we use some specific terms to refer to ActivityPub items. They are essentially our specific implementations of well-known ActivityPub concepts:
Before reading this, have a look at our [Federation Overview](overview.md) to get an idea how Lemmy federation works on a high level.
- Community: `Group`
- User: `Person`
- Post: `Page`
- Comment: `Note`
<!-- toc -->
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.
- [Context](#context)
- [Actors](#actors)
- [Community](#community)
- [User](#user)
- [Instance](#instance)
- [Objects](#objects)
- [Post](#post)
- [Comment](#comment)
- [Private Message](#private-message)
- [Collections](#collections)
- [Community Outbox](#community-outbox)
- [Community Followers](#community-followers)
- [Community Moderators](#community-moderators)
- [User Outbox](#user-outbox)
- [Activities](#activities)
- [User to Community](#user-to-community)
- [Follow](#follow)
- [Unfollow](#unfollow)
- [Report Post or Comment](#report-post-or-comment)
- [Community to User](#community-to-user)
- [Accept Follow](#accept-follow)
- [Announce](#announce)
- [Announcable](#announcable)
- [Create or Update Post](#create-or-update-post)
- [Create or Update Comment](#create-or-update-comment)
- [Like Post or Comment](#like-post-or-comment)
- [Dislike Post or Comment](#dislike-post-or-comment)
- [Undo Like or Dislike Post or Comment](#undo-like-or-dislike-post-or-comment)
- [Delete Post or Comment](#delete-post-or-comment)
- [Remove Post or Comment](#remove-post-or-comment)
- [Undo Delete or Remove](#undo-delete-or-remove)
- [Add Mod](#add-mod)
- [Remove Mod](#remove-mod)
- [Block User](#block-user)
- [Undo Block User](#undo-block-user)
- [User to User](#user-to-user)
- [Create or Update Private message](#create-or-update-private-message)
- [Delete Private Message](#delete-private-message)
- [Undo Delete Private Message](#undo-delete-private-message)
Sometimes you will see a notation like `Create/Note`. This refers to a `Create` activity with a `Note` as object.
<!-- tocstop -->
Below are explanations and examples for all actors, objects and activities from Lemmy. These include many optional fields which you can safely ignore.
## Context
@ -64,10 +29,6 @@ The context is identical for all activities and objects.
An automated actor. Users can send posts or comments to it, which the community forwards to its followers in the form of `Announce`.
Sends activities to user: `Accept/Follow`, `Announce`
Receives activities from user: `Follow`, `Undo/Follow`, `Create`, `Update`, `Like`, `Dislike`, `Remove` (only admin/mod), `Delete` (only creator), `Undo` (only for own actions)
```json
{{#include ../../../include/crates/apub/assets/lemmy/objects/group.json}}
```
@ -78,7 +39,7 @@ Receives activities from user: `Follow`, `Undo/Follow`, `Create`, `Update`, `Lik
| `name` | Title of the community |
| `sensitive` | True indicates that all posts in the community are nsfw |
| `attributedTo` | First the community creator, then all the remaining moderators |
| `content` | Text for the community sidebar, usually containing a description and rules |
| `summary` | Text for the community sidebar, usually containing a description and rules |
| `icon` | Icon, shown next to the community name |
| `image` | Banner image, shown on top of the community page |
| `inbox` | ActivityPub inbox URL |
@ -91,13 +52,7 @@ Receives activities from user: `Follow`, `Undo/Follow`, `Create`, `Update`, `Lik
### User
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.
Sends activities to Community: `Follow`, `Undo/Follow`, `Create`, `Update`, `Like`, `Dislike`, `Remove` (only admin/mod), `Delete` (only creator), `Undo` (only for own actions)
Receives activities from Community: `Accept/Follow`, `Announce`
Sends and receives activities from/to other users: `Create/Note`, `Update/Note`, `Delete/Note`, `Undo/Delete/Note` (all those related to private messages)
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/objects/person.json}}
@ -107,7 +62,7 @@ Sends and receives activities from/to other users: `Create/Note`, `Update/Note`,
| ------------------- | -------------------------------------------------------- |
| `preferredUsername` | Name of the actor |
| `name` | The user's displayname |
| `content` | User bio |
| `summary` | User bio |
| `icon` | The user's avatar, shown next to the username |
| `image` | The user's banner, shown on top of the profile |
| `inbox` | ActivityPub inbox URL |
@ -116,8 +71,6 @@ Sends and receives activities from/to other users: `Create/Note`, `Update/Note`,
| `updated` | Datetime when the user profile was last changed |
| `publicKey` | The public key used to verify signatures from this actor |
The user inbox is not actually implemented yet, and is only a placeholder for ActivityPub implementations which require it.
### Instance
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.
@ -143,7 +96,7 @@ Represents a Lemmy instance, and is used to federate global data like the instan
### Post
A page with title, and optional URL and text content. The URL often leads to an image, in which case a thumbnail is included. Each post belongs to exactly one community.
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/objects/page.json}}
@ -153,9 +106,9 @@ A page with title, and optional URL and text content. The URL often leads to an
| ----------------- | --------------------------------------------------------------------------------------------------- |
| `attributedTo` | ID of the user which created this post |
| `to` | ID of the community where it was posted to |
| `name` | Title of the post |
| `name` | Title of the post (mandatory) |
| `content` | Body of the post |
| `url` | An arbitrary link to be shared |
| `attachment` | A single website or image link |
| `image` | Thumbnail for `url`, only present if it is an image link |
| `commentsEnabled` | False indicates that the post is locked, and no comments can be added |
| `sensitive` | True marks the post as NSFW, blurs the thumbnail and hides it from users with NSFW settign disabled |
@ -172,11 +125,11 @@ A reply to a post, or reply to another comment. Contains only text (including re
```
| Field Name | Description |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `attributedTo` | ID of the user who created the comment |
| `to` | Community where the comment was made |
| `content` | The comment text |
| `inReplyTo` | IDs of the post where this comment was made, and the parent comment. If this is a top-level comment, `inReplyTo` only contains the post |
| `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. |
| `published` | Datetime when the comment was created |
| `updated` | Datetime when the comment was edited (not present if it was never edited) |
@ -216,12 +169,24 @@ The followers collection is only used to expose the number of followers. Actor I
### Community Moderators
List of moderators who can perform actions like removing posts or banning users.
```json
{{#include ../../../include/crates/apub/assets/lemmy/collections/group_moderators.json}}
```
### Community Featured Posts
List of posts which are stickied in the community.
```json
{{#include ../../../include/crates/apub/assets/lemmy/collections/group_featured_posts.json}}
```
### User Outbox
Only contains `totalItems` count, but no actual `items` for privacy reasons.
```json
{{#include ../../../include/crates/apub/assets/lemmy/collections/person_outbox.json}}
```
@ -232,7 +197,7 @@ The followers collection is only used to expose the number of followers. Actor I
#### Follow
When the user clicks "Subscribe" in a community, a `Follow` is sent. The community automatically responds with an `Accept/Follow`.
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/following/follow.json}}
@ -240,45 +205,15 @@ When the user clicks "Subscribe" in a community, a `Follow` is sent. The communi
#### Unfollow
Clicking on the unsubscribe button in a community causes an `Undo/Follow` to be sent. The community removes the user from its follower list after receiving it.
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/following/undo_follow.json}}
```
#### Report Post or Comment
Reports a post or comment for rule violation, so that mods/admins review it.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/report_page.json}}
```
### Community to User
#### Accept Follow
Automatically sent by the community in response to a `Follow`. At the same time, the community adds this user to its followers list.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/following/accept.json}}
```
#### Announce
When the community receives a post or comment activity, it wraps that into an `Announce` and sends it to all followers.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/announce_create_page.json}}
```
### Announcable
All of these activities are sent from a user to a community. The community then wraps it in an Announce activity, and sends it to its followers.
#### Create or Update Post
When a user creates a new post, it is sent to the respective community. Editing a previously created post sends an almost identical activity, except the `type` being `Update`. We don't support mentions in posts yet.
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`.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/create_or_update/create_page.json}}
@ -286,7 +221,10 @@ When a user creates a new post, it is sent to the respective community. Editing
#### Create or Update Comment
A reply to a post, or to another comment. Can contain mentions of other users. Editing a previously created post sends an almost identical activity, except the `type` being `Update`.
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`.
The origin instance also scans the Comment for any User mentions, and sends the `Create/Note` to
those Users as well.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/create_or_update/create_note.json}}
@ -310,7 +248,7 @@ A downvote for a post or comment.
#### Undo Like or Dislike Post or Comment
Remove a vote that was previously done by the same user.
Revert a vote that was previously done by the same user.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/voting/undo_like_note.json}}
@ -318,29 +256,109 @@ Remove a vote that was previously done by the same user.
#### Delete Post or Comment
Deletes a previously created post or comment. This can only be done by the original creator of that post/comment.
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.
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/deletion/delete_page.json}}
```
#### Undo Delete
Post or comment deletions can be reverted by the same user.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json}}
```
#### Report Post, comment or private message
Reports content for rule violation, so that mods/admins can review it.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/report_page.json}}
```
#### Delete User
Sent when a user deletes his own account.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/deletion/delete_user.json}}
```
### Community to User
#### Accept Follow
Automatically sent by the community in response to a `Follow`. At the same time, the community adds this user to its followers list.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/following/accept.json}}
```
#### Announce
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/announce_create_page.json}}
```
### Moderation
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.
#### Remove Post or Comment
Removes a post or comment. This can only be done by a community mod, or by an admin on the instance where the community is hosted. The difference to delete is that remove activities have a summary field, which contains the reason for removal, as provided by the mod/admin.
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/deletion/remove_note.json}}
```
#### Undo Delete or Remove
#### Block User
Reverts the action done by the activity in the object field. In this example, the removed Note is restored.
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/block/block_user.json}}
```
#### Lock post
Posts can be locked so that no new comments can be created.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/lock_page.json}}
```
#### Undo mod actions
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json}}
```
#### Add Mod
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/block/undo_block_user.json}}
```
#### Add or remove featured post
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/add_featured_post.json}}
```
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/remove_featured_post.json}}
```
#### Add or remove mod
Add a new mod to the community. Has to be sent by an existing community mod, or an admin of the community's instance.
@ -348,36 +366,25 @@ Add a new mod to the community. Has to be sent by an existing community mod, or
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/add_mod.json}}
```
#### Remove Mod
Remove an existing mod from the community. Has to be sent by an existing community mod, or an admin of the community's instance.
An existing mod can be removed in the same way.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/community/remove_mod.json}}
```
#### Block User
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.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/block/block_user.json}}
```
#### Undo Block User
Reverts a previous user block.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/block/undo_block_user.json}}
```
### User to User
#### Follow a user
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.
```json
{{#include ../../../include/crates/apub/assets/pleroma/activities/follow.json}}
```
#### Create or Update Private message
Creates a new private message between two users.
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`.
```json
{{#include ../../../include/crates/apub/assets/lemmy/activities/create_or_update/create_private_message.json}}

View file

@ -1 +0,0 @@
# Federation

View file

@ -1,126 +0,0 @@
# Federation Overview
This document is for anyone who wants to know how Lemmy federation works, without being overly technical. It is meant provide a high-level overview of ActivityPub federation in Lemmy. If you are implementing ActivityPub yourself and want to be compatible with Lemmy, read our [ActivityPub Protocol](lemmy_protocol.md).
## Documentation conventions
To keep things simple, sometimes you will see things formatted like `Create/Note` or `Delete/Event` or `Undo/Follow`. The thing before the slash is the Activity, and the thing after the slash is the Object inside the Activity, in an `object` property. So these are to be read as follows:
- `Create/Note`: a `Create` activity containing a `Note` in the `object` field
- `Delete/Event`: a `Delete` activity containing an `Event` in the `object` field
- `Undo/Follow`: an `Undo` activity containing a `Follow` in the `object` field
In Lemmy we use some specific terms to refer to ActivityPub items. They are essentially our specific implementations of well-known ActivityPub concepts:
- Community: `Group`
- User: `Person`
- Post: `Page`
- Comment: `Note`
This document has three main sections:
- **Federation philosophy** lays out the general model of how this is intended to federate
- **User Activities** describes which actions that a User can take to interact
- **Community Activities** describes what the Community does in response to certain User actions
## Federation philosophy
The primary Actor in Lemmy is the Community. Each community resides on a single instance, and consists of a list of Posts and a list of followers. The primary interaction is that of a User sending a Post or Comment related activity to the Community inbox, which then announces it to all its followers.
Each Community has a specific creator User, who is responsible for setting rules, appointing moderators, and removing content that violates the rules.
Besides moderation on the community level, each instance has a set of administrator Users, who have the power to do site-wide removals and bans.
Users follow Communities that they are interested in, in order to receive Posts and Comments. They also vote on Posts and Comments, as well as creating new ones. Comments are organised in a tree structure and commonly sorted by number of votes. Direct messages between Users are also supported.
Users can not follow each other, and neither can Communities follow anything.
Our federation implementation is already feature complete, but so far we haven't focused at all on complying with the ActivityPub spec. As such, Lemmy is likely not compatible with implementations which expect to send and receive valid activities. This is something we plan to fix in the near future. Check out [#698](https://github.com/LemmyNet/lemmy/issues/698) for an overview of our deviations.
## User Activities
### Follow a Community
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.
### Unfollow a Community
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.
### Create a Post
When a user creates a new Post in a given Community, it is sent as `Create/Page` to the Community
inbox.
### Create a Comment
When a new Comment is created for a Post, both the Post ID and the parent Comment ID (if it exists)
are written to the `inReplyTo` field. This allows assigning it to the correct Post, and building
the Comment tree. It is then sent to the Community inbox as `Create/Note`
The origin instance also scans the Comment for any User mentions, and sends the `Create/Note` to
those Users as well.
### Edit a Post
Changes the content of an existing Post. Can only be done by the creating User.
### Edit a Comment
Changes the content of an existing Comment. Can only be done by the creating User.
### Likes and Dislikes
Users can like or dislike any Post or Comment. These are sent as `Like/Page`, `Dislike/Note` etc to the Community inbox.
### Deletions
The creator of a Post, Comment or Community can delete it. It is then sent to the Community followers. The item is then hidden from all users.
### Removals
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.
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.
### Revert a previous Action
We don't delete anything from our database, just hide it from users. Deleted or removed Communities/Posts/Comments have a "restore" button. This button generates an `Undo` activity which sets the original delete/remove activity as object, such as `Undo/Remove/Post` or `Undo/Delete/Community`.
Clicking on the upvote button of an already upvoted post/comment (or the downvote button of an already downvoted post/comment) also generates an `Undo`. In this case and `Undo/Like/Post` or `Undo/Dislike/Comment`.
### Create private message
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/Note` to the user inbox. Private messages can only be directed at a single User.
### Edit private message
`Update/Note` changes the text of a previously sent message
### Delete private message
`Delete/Note` deletes a private message.
### Restore private message
`Undo/Delete/Note` reverts the deletion of a private message.
## Community Activities
The Community is essentially a bot, which will only do anything in reaction to actions from Users. The User who first created the Community becomes the first moderator, and can add additional moderators. In general, whenever the Community receives a valid activity in its inbox, that activity is forwarded to all its followers.
### Accept follow
If the Community receives a `Follow` activity, it automatically responds with `Accept/Follow`. It also adds the User to its list of followers.
### Unfollow
Upon receiving an `Undo/Follow`, the Community removes the User from its followers list.
### Announce
If the Community receives any Post or Comment related activity (Create, Update, Like, Dislike, Remove, Delete, Undo), it will Announce this to its followers. For this, an Announce is created with the Community as actor, and the received activity as object. Following instances thus stay updated about any actions in Communities they follow.
### Delete Community
If the creator or an admin deletes the Community, it sends a `Delete/Group` to all its followers.