About The Project

Front PagePost
main screenchat screen

Lemmy is similar to sites like Reddit, Lobste.rs, Raddle, or Hacker News: you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the Fediverse.

The word “fediverse” (federated universe) refers to the network of all Lemmy servers and other projects, users of which are able to talk to each other seamlessly.

Like email, whether you sign up on GMail or Outlook, you know you’ll be able to email everyone you need to, as long as you know their address. On Lemmy you're able to subscribe to communities on any other server, and can have discussions with users registered elsewhere.

The overall goal is to create an easily self-hostable, decentralized alternative to reddit and other link aggregators, without a single company running the show.

There are no shareholders, and no targeted advertising: only people sharing the things they want to share with each other.

Each lemmy server can set its own moderation policy; appointing site-wide admins, and community moderators to keep out the trolls, and foster a healthy, non-toxic environment where all can feel comfortable contributing.

Note: Federation is still in active development

Why's it called Lemmy?

Built With

Features

  • Open source, AGPL License.
  • Self hostable, easy to deploy.
  • Clean, mobile-friendly interface.
    • Only a minimum of a username and password is required to sign up!
    • User avatar support.
    • Live-updating Comment threads.
    • Full vote scores (+/-) like old reddit.
    • Themes, including light, dark, and solarized.
    • Emojis with autocomplete support. Start typing :
    • User tagging using @, Community tagging using !.
    • Integrated image uploading in both posts and comments.
    • A post can consist of a title and any combination of self text, a URL, or nothing else.
    • Notifications, on comment replies and when you're tagged.
      • Notifications can be sent via email.
    • i18n / internationalization support.
    • RSS / Atom feeds for All, Subscribed, Inbox, User, and Community.
  • Cross-posting support.
    • A similar post search when creating new posts. Great for question / answer communities.
  • Moderation abilities.
    • Public Moderation Logs.
    • Can sticky posts to the top of communities.
    • Both site admins, and community moderators, who can appoint other moderators.
    • Can lock, remove, and restore posts and comments.
    • Can ban and unban users from communities and the site.
    • Can transfer site and communities to others.
  • Can fully erase your data, replacing all posts and comments.
  • NSFW post / community support.
  • High performance.
    • Server is written in rust.
    • Front end is ~80kB gzipped.
    • Front end works without javascript (read-only).
    • Supports arm64 / Raspberry Pi.

Trending / Hot / Best Sorting algorithm

Goals

  • During the day, new posts and comments should be near the top, so they can be voted on.
  • After a day or so, the time factor should go away.
  • Use a log scale, since votes tend to snowball, and so the first 10 votes are just as important as the next hundred.

Implementations

Reddit

Does not take the lifetime of the thread into account, giving early comments an overwhelming advantage over later ones, 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.

Hacker News

While far superior to Reddit's implementation for its decay of scores over time, Hacker News' ranking algorithm does not use a logarithmic scale for scores.

Lemmy

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.

Rank = ScaleFactor * log(Max(1, 3 + Score)) / (Time + 2)^Gravity

Score = Upvotes - Downvotes
Time = time since submission (in hours)
Gravity = Decay gravity, 1.8 is default
  • Lemmy uses the same Rank algorithm above, in two sorts: Active, and Hot.
    • Active uses the post votes, and latest comment time (limited to two days).
    • Hot uses the post votes, and the post published time.
  • Use Max(1, score) to make sure all comments are affected by time decay.
  • 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.
  • The sign and abs of the score are necessary for dealing with the log of negative scores.
  • A scale factor of 10k gets the rank in integer form.

A plot of rank over 24 hours, of scores of 1, 5, 10, 100, 1000, with a scale factor of 10k.

Active User counts

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.

An active user is someone who has posted or commented 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.

Lemmy Quick Start Guide

So you want to join Lemmy and get posting. Great! Here's how to dive straight in.

Where do I sign up?

The first thing you have to do is choose your server. This is an extra step compared to sites like Reddit, but isn’t as difficult as it may seem.

Like with email, your identity is hosted by the server you sign up on. So for example, I joined lemmy.ml so to mention me you can type @retiolus@lemmy.ml in your post.

If what you want to talk about most fits into a clear category (maybe it’s video games or art or queer life or coding or fiction, etc) then it might be worth making your first server one where that will primarily host that sort of content – it’ll be easier to make connections and find like-minded folk. Think of your server as a neighbourhood or a venue, where the general chatter can have a specific focus.

You have the ability to view all public local posts that have been made by people on your server in the “Local” tab.

If you aren’t here to stick mainly to one topic, you’ll likely want to pick a server that caters to general interests. Either way, there’s a helpful servers list on join-lemmy.org.

Don’t panic! You’ll be able to chatter to people on other servers, no matter which one you choose. Remember, this is like email, and you can still email your mum on her ancient Yahoo account from your GMail, for example.

Knowing your server

Take a moment before registering to check out the rules of your chosen server and make sure they are compatible with how you want to be posting.

Servers usually define their rules in a simple way on the sidebar on the right of the site. From top to bottom, you will usually find the list of "Trending communities" followed by "Subscribed to communities", a brief description of the server, the rules, some stats and the username of the administrators.

Admins are super friendly people who are usually running the server out of their own pocket, so it’s good to get to know them. Many accept donations to cover the running costs and if you’re able to contribute then it’s always appreciated.

I think I found my new home!

Head to the Login page (https://your.server/login) of your server and choose your username and password in the sign up form. You don't need an email to register, but this is the only way to reset your password if you lose it.

Next thing to do is upload your profile picture, give the settings page (https://your.server/settings) a good once-over (and do come back to it when you’ve been on Lemmy a week or so just to make any tweaks that might help your experience) and get ready to introduce yourself.

Some interesting settings worth checking are: your default feed sorting filter between subscribed, local or all and your default sorting type.

While you can simply enjoy reading Lemmy, the real fun begins as you dive in and start participating yourself by posting, voting and commenting.

To share a post, click the “Create Post” button on the top side of the screen. You’ll be taken to a form where you can enter a URL of the link or upload your image, and input a title and body for the post. You will also need to enter the community where you’d like your post to appear. Before doing so, it’s a good idea to head to that communitie’s page and read the posting guidelines that appear on the right side of the screen. This will ensure you are abiding by the particular ethos of the community. For example, on the !privacy@lemmy.ml community, you are not allowed to promote proprietary software.

When all the fields are filled out, click “Create” and your post will go live.

By writing a new post or comment, start typing...

  • @a_user_name to get a list of usernames.
  • !a_community to get a list of communities.
  • :emoji to get a list of emojis.

A quick tour of the web interface

interface.png

The standard Lemmy interface has a single feed. You can change the content of the feed by using the filters:

Subscribed includes the posts of people on your server and on other servers, all that matters is that it’s community you follow.

Local is the feed of all the posts of communities on your server. On many servers, particularly smaller ones and ones focused on a particular topic, this is where the magic happens. You can reply to people from there, and it’s a great place to meet people who share the same interests as you.

All, or the federated timeline, is a view of all the public posts your servers knows about from across the whole network (including local ones). The most common reason that something appears in the federated timeline is that someone from your server might follow a community that's on another server.

TypeDescription
PostsOnly displays publications
CommentsDisplays only comments
------
ActiveTrending sort based on the score, and the most recent comment time.
HotTrending sort based on the score, and the post creation time.
NewThe newest items.
Most CommentsThe posts with the most comments.
New CommentsThe posts with most recent comments, IE a forum-style sort.
TopThe highest scoring items in the given time frame.

For more detail, check the Post and Comment Ranking details.

Using Markdown

Lemmy's posts are done using Markdown, there are of course shortcuts for you to use when writing but here is a table to help you if you want to go further.

TypeOr… to Get
*Italic*_Italic_Italic
**Bold**__Bold__Bold
# Heading 1Heading 1
=========

Heading 1

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

[1]: http://b.org
Link
![Image](http://url/a.png)![Image][1]

[1]: http://url/b.jpg
Markdown
> Blockquote
Blockquote
* List
* List
* List
- List
- List
- List
* List
* List
* List
1. One
2. Two
3. Three
1) One
2) Two
3) Three
1. One
2. Two
3. Three
Horizontal Rule
---
Horizontal Rule
***
Horizontal Rule

`Inline code` with backticksInline code with backticks
```
# 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'
::: spoiler hidden or nsfw stuff
a bunch of spoilers here
:::
hidden or nsfw stuff

a bunch of spoilers here

Some ~subscript~ textSome subscript text
Some ^superscript^ textSome superscript text

CommonMark Tutorial

Administration info

Information for Lemmy instance admins, and those who want to run a server.

Install

Official/Supported methods

Lemmy has two primary installation methods:

We recommend using Ansible, because it simplifies the installation and also makes updating easier.

Other installation methods

⚠️ Under your own risk.

In some cases, it might be necessary to use different installation methods. But we don't recommend this, and can't provide support for them.

Docker Installation

Make sure you have both docker and docker-compose(>=1.24.0) installed. On Ubuntu, just run apt install docker-compose docker.io. Next,

# create a folder for the lemmy files. the location doesnt matter, you can put this anywhere you want
mkdir /lemmy
cd /lemmy

# download default config files
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/prod/docker-compose.yml
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/lemmy.hjson

# Set correct permissions for pictrs folder
mkdir -p volumes/pictrs
sudo chown -R 991:991 volumes/pictrs

Open up your docker-compose.yml, and make sure LEMMY_EXTERNAL_HOST for lemmy-ui is set to your correct host.

- LEMMY_INTERNAL_HOST=lemmy:8536
- LEMMY_EXTERNAL_HOST=your-domain.com
- LEMMY_HTTPS=false

If you'd like a different database password, you should also change it in the docker-compose.yml before your first run.

After this, have a look at the config file named lemmy.hjson, and adjust it, in particular the hostname, and possibly the db password. Then run:

docker-compose up -d

You can access the lemmy-ui at http://localhost:1235

To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. A sample nginx config, could be setup with:

wget https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/templates/nginx.conf
# Replace the {{ vars }}
# The default lemmy_port is 8536
# The default lemmy_ui_port is 1235
sudo mv nginx.conf /etc/nginx/sites-enabled/lemmy.conf

You will also need to setup TLS, for example with Let's Encrypt. After this you need to restart Nginx to reload the config.

Updating

To update to the newest version, you can manually change the version in docker-compose.yml. Alternatively, fetch the latest version from our git repo:

wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/prod/docker-compose.yml
docker-compose up -d

Ansible Installation

Follow the instructions on the Lemmy-Ansible repo.

From Scratch

⚠️ 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.

These instructions are written for Ubuntu 20.04.

Installation

Lemmy Backend

It is built from source, so this may take a while, especially on slow devices. For example, Lemmy v0.12.2 takes 17 minutes to build on a dual core VPS. If you prefer prebuilt binaries, use Docker.

Compile and install Lemmy, setup database:

apt install pkg-config libssl-dev libpq-dev cargo postgresql
# installs latest release, you can also specify one with --version
cargo install lemmy_server --target-dir /usr/bin/
# replace db-passwd with a randomly generated password
sudo -iu postgres psql -c "CREATE USER lemmy WITH PASSWORD 'db-passwd';"
sudo -iu postgres psql -c "CREATE DATABASE lemmy WITH OWNER lemmy;"
adduser lemmy --system --disabled-login --no-create-home --group

Minimal Lemmy config, put this in /etc/lemmy/lemmy.hjson (see here for more config options). Run chown lemmy:lemmy /etc/lemmy/ -R to set the correct owner.

{
  database: {
    # put your db-passwd from above
    password: "db-passwd"
  }
  # replace with your domain
  hostname: example.com
  bind: "127.0.0.1"
  # put a random string here (required for login token encryption)
  jwt_secret: "changeme"
}

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, then run systemctl enable lemmy and systemctl start lemmy.

[Unit]
Description=Lemmy - A link aggregator for the fediverse
After=network.target

[Service]
User=lemmy
ExecStart=/usr/bin/lemmy_server
Environment=LEMMY_CONFIG_LOCATION=/etc/lemmy/lemmy.hjson
Restart=on-failure

# Hardening
ProtectSystem=yes
PrivateTmp=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

If you did everything right, the Lemmy logs from 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}.

Install lemmy-ui (web frontend)

Install dependencies (nodejs and yarn in Ubuntu 20.04 repos are too old)

# https://classic.yarnpkg.com/en/docs/install/#debian-stable
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
# https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions
curl -fsSL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt install nodejs yarn

Clone the git repo, checkout the version you want (0.12.2 in this case), and compile it.

mkdir /var/lib/lemmy-ui
cd /var/lib/lemmy-ui
chown lemmy:lemmy .
# dont compile as admin
sudo -u lemmy bash
git clone https://github.com/LemmyNet/lemmy-ui.git --recursive .
git checkout 0.12.2 # replace with the version you want to install
yarn install --pure-lockfile
yarn build:prod
exit

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, then run systemctl enable lemmy-ui and systemctl start lemmy-ui.

[Unit]
Description=Lemmy UI - Web frontend for Lemmy
After=lemmy.service
Before=nginx.service

[Service]
User=lemmy
WorkingDirectory=/var/lib/lemmy-ui
ExecStart=/usr/bin/node dist/js/server.js
Environment=LEMMY_INTERNAL_HOST=localhost:8536
Environment=LEMMY_EXTERNAL_HOST=example.com
Environment=LEMMY_HTTPS=true
Restart=on-failure

# Hardening
ProtectSystem=full
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

If everything went right, the command curl -I localhost:1234 should show 200 OK at the top.

Configure reverse proxy and TLS

Install dependencies

apt install nginx certbot python3-certbot-nginx

Request Let's Encrypt TLS certificate (just follow the instructions)

certbot certonly --nginx

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.

@daily certbot certonly --nginx --cert-name example.com -d example.com --deploy-hook 'nginx -s reload'

Finally, add the nginx config. After downloading, you need to replace some variables in the file.

curl https://raw.githubusercontent.com/LemmyNet/lemmy-ansible/main/templates/nginx.conf \
    --output /etc/nginx/sites-enabled/lemmy.conf
# put your actual domain instead of example.com
sed -i -e 's/{{domain}}/example.com/g' /etc/nginx/sites-enabled/lemmy.conf
sed -i -e 's/{{lemmy_port}}/8536/g' /etc/nginx/sites-enabled/lemmy.conf
sed -i -e 's/{{lemmy_ui_port}}/1234/g' /etc/nginx/sites-enabled/lemmy.conf
nginx -s reload

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.

Pict-rs (for image hosting, optional)

Pict-rs requires a newer Rust version than the one available in Ubuntu 20.04 repos. So you need to install Rustup which installs the toolchain for you.

apt install ffmpeg exiftool libgexiv2-dev --no-install-recommends
adduser pictrs --system --disabled-login --no-create-home --group
mkdir /var/lib/pictrs-source
cd /var/lib/pictrs
git clone https://git.asonix.dog/asonix/pict-rs.git .
# check docker-compose.yml for pict-rs version used by lemmy
# https://github.com/LemmyNet/lemmy-ansible/blob/main/templates/docker-compose.yml#L40 
git checkout v0.2.6-r2
# or simply add the bin folder to your $PATH
$HOME/.cargo/bin/cargo build --release
cp target/release/pict-rs /usr/bin/
# create folder to store image data
mkdir /var/lib/pictrs
chown pictrs:pictrs /var/lib/pictrs

Pict-rs 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.

wget https://download.imagemagick.org/ImageMagick/download/binaries/magick
# compare hash with the "message digest" on the official page linked above
sha256sum magick
mv magick /usr/bin/
chmod 755 /usr/bin/magick

Just like before, place the config below in /etc/systemd/system/pictrs.service, then run systemctl enable pictrs and systemctl start pictrs.

[Unit]
Description=pict-rs - A simple image host
After=network.target

[Service]
User=pictrs
ExecStart=/usr/bin/pict-rs
Environment=PICTRS_PATH=/var/lib/pictrs
Environment=PICTRS_ADDR=127.0.0.1:8080
Restart=on-failure

[Install]
WantedBy=multi-user.target

If it is working correctly, curl 127.0.0.1:8080 should output nothing (particularly no errors).

Now add the line pictrs_url: "http://127.0.0.1:8080" to /etc/lemmy/lemmy.hjson so that Lemmy knows how to reach Pict-rs. Then restart Lemmy with systemctl restart lemmy, and image uploads should be working.

Upgrading

Lemmy

# installs latest release, you can also specify one with --version
cargo install lemmy_server --target-dir /usr/bin/
systemctl restart lemmy

Lemmy UI

cd /var/lib/lemmy-ui
sudo -u lemmy
git checkout main
git pull --tags
git checkout 0.12.2 # replace with the version you want to install
git submodule update
yarn install --pure-lockfile
yarn build:prod
exit
systemctl restart lemmy-ui

Pict-rs

rustup update
cd /var/lib/pictrs-source
git checkout main
git pull --tags
# check docker-compose.yml for pict-rs version used by lemmy
# https://github.com/LemmyNet/lemmy-ansible/blob/main/templates/docker-compose.yml#L40 
git checkout v0.2.6-r2
# or simply add the bin folder to your $PATH
$HOME/.cargo/bin/cargo build --release
cp target/release/pict-rs /usr/bin/
systemctl restart pictrs

Installing on AWS

⚠️ 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.

Lemmy AWS CDK

This contains the necessary infrastructure definitions to deploy Lemmy to AWS using their Cloud Development Kit.

Included:

  • ECS fargate cluster
    • Lemmy-UI
    • Lemmy
    • Pictrs
  • CloudFront CDN
  • EFS storage for image uploads
  • Aurora Serverless Postgres DB
  • Bastion VPC host
  • Load balancers for Lemmy
  • DNS records for your site

Quickstart

Clone the Lemmy-CDK.

Clone Lemmy and Lemmy-UI to the directory above this.

cp example.env.local .env.local
# edit .env.local

You should edit .env.local with your site settings.

npm install -g aws-cdk
npm install
cdk bootstrap
cdk deploy

Cost

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.

Useful CDK commands

  • npm run build compile typescript to js
  • npm run watch watch for changes and compile
  • npm run test perform the jest unit tests
  • cdk deploy deploy this stack to your default AWS account/region
  • cdk diff compare deployed stack with current state
  • cdk synth emits the synthesized CloudFormation template

Administration First Steps

After you successfully installed Lemmy either manually with Docker or automatically with Ansible here are some recommendations for a new administrator of a Lemmy server.

Admin Settings

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.

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, where you can find information about your database, the email used by the server, the federation options or who is the main administrator.

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 for more information on how to do this.

Check that everything is working properly

Email

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.

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.

Federation

Federation is disabled by default, and needs to be enabled either through the online admin panel or directly through the config.json file.

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.

Inclusion on join-lemmy.org instance list

To be included in the list of Lemmy instances on join-lemmy.org you must meet the following requirements:

  • Federate with at least one instance from the list
  • Have a site description and icon
  • Be patient and wait the site to be updated, there's no fixed schedule for that

In the meantime you can always promote your server on other social networks like Mastodon using the hashtag #Lemmy.

Keeping up to date

You can subscribe to the Github RSS feeds to be informed of the latest releases:

There is also a Matrix chat 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.

Configuration

The configuration is based on the file config.hjson, which is located by default at config/config.hjson. To change the default location, you can set the environment variable LEMMY_CONFIG_LOCATION.

An additional environment variable LEMMY_DATABASE_URL is available, which can be used with a PostgreSQL connection string like postgres://lemmy:password@lemmy_db:5432/lemmy, passing all connection details at once.

If the Docker container is not used, manually create the database specified above by running the following commands:

cd server
./db-init.sh

Federation is not set up by default. You can add this this federation block to your lemmy.hjson, and ask other servers to add you to their allowlist.

Full config with default values

{
  # settings related to the postgresql database
  database: {
    # Username to connect to postgres
    user: "lemmy"
    # Password to connect to postgres
    password: "password"
    # Host where postgres is running
    host: "localhost"
    # Port where postgres can be accessed
    port: 5432
    # Name of the postgres database for lemmy
    database: "lemmy"
    # Maximum number of active sql connections
    pool_size: 5
  }
  # rate limits for various user actions, by user ip
  rate_limit: {
    # Maximum number of messages created in interval
    message: 180
    # Interval length for message limit, in seconds
    message_per_second: 60
    # Maximum number of posts created in interval
    post: 6
    # Interval length for post limit, in seconds
    post_per_second: 600
    # Maximum number of registrations in interval
    register: 3
    # Interval length for registration limit, in seconds
    register_per_second: 3600
    # Maximum number of image uploads in interval
    image: 6
    # Interval length for image uploads, in seconds
    image_per_second: 3600
  }
  # Settings related to activitypub federation
  federation: {
    # Whether to enable activitypub federation.
    enabled: false
    # Allows and blocks are described here:
    # https://join-lemmy.org/docs/en/federation/administration.html///instance-allowlist-and-blocklist
    # 
    # list of instances with which federation is allowed
    allowed_instances: [
      instance1.tld
      instance2.tld
      /* ... */
    ]
    # Instances which we never federate anything with (but previously federated objects are unaffected)
    blocked_instances: [
      string
      /* ... */
    ]
    # If true, only federate with instances on the allowlist and block everything else. If false
    # use allowlist only for remote communities, and posts/comments in local communities
    # (meaning remote communities will show content from arbitrary instances).
    strict_allowlist: true
  }
  captcha: {
    # Whether captcha is required for signup
    enabled: false
    # Can be easy, medium, or hard
    difficulty: "medium"
  }
  # Email sending configuration. All options except login/password are mandatory
  email: {
    # Hostname and port of the smtp server
    smtp_server: "localhost:25"
    # Login name for smtp server
    smtp_login: "string"
    # Password to login to the smtp server
    smtp_password: "string"
    # Address to send emails from, eg noreply@your-instance.com
    smtp_from_address: "noreply@example.com"
    # Whether or not smtp connections should use tls
    use_tls: true
  }
  # Parameters for automatic configuration of new instance (only used at first start)
  setup: {
    # Username for the admin user
    admin_username: "admin"
    # Password for the admin user
    admin_password: "my_passwd"
    # Name of the site (can be changed later)
    site_name: "My Lemmy Instance"
    # Email for the admin user (optional, can be omitted and set later through the website)
    admin_email: "string"
    sidebar: "string"
    description: "string"
    icon: "string"
    banner: "string"
    enable_downvotes: true
    open_registration: true
    enable_nsfw: true
    community_creation_admin_only: true
  }
  # the domain name of your instance (mandatory)
  hostname: "unset"
  # Address where lemmy should listen for incoming requests
  bind: "0.0.0.0"
  # Port where lemmy should listen for incoming requests
  port: 8536
  # Whether the site is available over TLS. Needs to be true for federation to work.
  tls_enabled: true
  # Address where pictrs is available (for image hosting)
  pictrs_url: "http:#localhost:8080"
  slur_filter: "(\bThis\b)|(\bis\b)|(\bsample\b)"
  # Maximum length of local community and user names
  actor_name_max_length: 20
}

Federation

Lemmy has three types of federation:

  • Allowlist: Explicitly list instances to connect to.
  • BlockList: Explicitly list instances to not connect to. Federation is open to all other instances.
  • Open: Federate with all potential instances.

Federation is not set up by default. You can add this this federation block to your lemmy.hjson, and ask other servers to add you to their allowlist.

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 amount of different actions possible on social media (post, comment, like, share, etc) means that ActivityPub is much more complicated than SMTP.

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.

One way you can take advantage of federation is by opening a different instance, like ds9.lemmy.ml, and browsing it. If you see an interesting community, post or user that you want to interact with, just copy its URL and paste it into the search of your own instance. Your instance will connect to the other one (assuming the allowlist/blocklist allows it), and directly display the remote content to you, so that you can follow a community or comment on a post. Here are some examples of working searches:

  • !main@lemmy.ml (Community)
  • @nutomic@lemmy.ml (User)
  • https://lemmy.ml/c/programming (Community)
  • https://lemmy.ml/u/nutomic (User)
  • https://lemmy.ml/post/123 (Post)
  • https://lemmy.ml/comment/321 (Comment)

You can see the list of linked instances by following the "Instances" link at the bottom of any Lemmy page.

Fetching communities

If you search for a community first time, 20 posts are fetched initially. Only if a least one user on your instance subscribes to the remote community, will the community send updates to your instance. Updates include:

  • New posts, comments
  • Votes
  • Post, comment edits and deletions
  • Mod actions

You can copy the URL of the community from the address bar in your browser and insert it in your search field. Wait a few seconds, the post will appear below. At the moment there is no loading indicator for the search, so wait a few seconds if it shows "no results".

Fetching posts

Paste the URL of a post into your Lemmy instance's search field. Wait a few seconds until the post appears. This will also fetch the community profile, and the profile of the post creator.

Fetching comments

If you find an interesting comment under a posting on another instance, you can find below the comment in the 3-dot-menu the link-symbol. Copy this link. It looks like https://lemmy.ml/post/56382/comment/40796. Remove the post/XXX part and put it into your search-bar. For this example, search for https://lemmy.ml/comment/40796. This comment, all parent comments, users and community and the corresponding post are fetched from the remote instance, if they are not known locally.

Sibling comments are not fetched! If you want more comments from older posts, you have to search for each of them as described above.

Troubleshooting

Different problems that can occur on a new instance, and how to solve them.

Many Lemmy features depend on a correct reverse proxy configuration. Make sure yours is equivalent to our nginx config.

General

Logs

For frontend issues, check the browser console for any error messages.

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.

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=verbose, then do docker-compose restart lemmy.

Creating admin user doesn't work

Make sure that websocket is working correctly, by checking the browser console for errors. In nginx, the following headers are important for this:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Rate limit error when many users access the site

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:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Federation

Other instances can't fetch local objects (community, post, etc)

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:

set $proxpass "http://0.0.0.0:{{ lemmy_ui_port }}";
if ($http_accept = "application/activity+json") {
set $proxpass "http://0.0.0.0:{{ lemmy_port }}";
}
if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
set $proxpass "http://0.0.0.0:{{ lemmy_port }}";
}
proxy_pass $proxpass;

You can test that it works correctly by running the following commands, all of them should return valid JSON:

curl -H "Accept: application/activity+json" https://your-instance.com/u/some-local-user
curl -H "Accept: application/activity+json" https://your-instance.com/c/some-local-community
curl -H "Accept: application/activity+json" https://your-instance.com/post/123 # the id of a local post
curl -H "Accept: application/activity+json" https://your-instance.com/comment/123 # the id of a local comment

Fetching remote objects works, but posting/commenting in remote communities fails

Check that federation is allowed on both instances.

Also ensure that the time is accurately set on your server. Activities are signed with a timestamp, and will be discarded if it is off by more than 10 seconds.

Backup and Restore Guide

Docker and Ansible

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.

Incremental Database backup

To incrementally backup the DB to an .sql file, you can run:

docker-compose exec postgres pg_dumpall -c -U lemmy >  lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql

A Sample backup script

#!/bin/sh
# DB Backup
ssh MY_USER@MY_IP "docker-compose exec postgres pg_dumpall -c -U lemmy" >  ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql

# Volumes folder Backup
rsync -avP -zz --rsync-path="sudo rsync" MY_USER@MY_IP:/LEMMY_LOCATION/volumes ~/BACKUP_LOCATION/FOLDERNAME

Restoring the DB

If you need to restore from a pg_dumpall file, you need to first clear out your existing database

# Drop the existing DB
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"

# Restore from the .sql backup
cat db_dump.sql  |  docker exec -i FOLDERNAME_postgres_1 psql -U lemmy # restores the db

# This also might be necessary when doing a db import with a different password.
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "alter user lemmy with password 'bleh'"

Changing your domain name

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.

Get into psql for your docker:

docker-compose exec postgres psql -U lemmy

-- Post
update post set ap_id = replace (ap_id, 'old_domain', 'new_domain');
update post set url = replace (url, 'old_domain', 'new_domain');
update post set body = replace (body, 'old_domain', 'new_domain');
update post set thumbnail_url = replace (thumbnail_url, 'old_domain', 'new_domain');

-- Comments
update comment set ap_id = replace (ap_id, 'old_domain', 'new_domain');
update comment set content = replace (content, 'old_domain', 'new_domain');

-- User
update user_ set actor_id = replace (actor_id, 'old_domain', 'new_domain');
update user_ set inbox_url = replace (inbox_url, 'old_domain', 'new_domain');
update user_ set shared_inbox_url = replace (shared_inbox_url, 'old_domain', 'new_domain');
update user_ set avatar = replace (avatar, 'old_domain', 'new_domain');

-- Community
update community set actor_id = replace (actor_id, 'old_domain', 'new_domain');
update community set followers_url = replace (followers_url, 'old_domain', 'new_domain');
update community set inbox_url = replace (inbox_url, 'old_domain', 'new_domain');
update community set shared_inbox_url = replace (shared_inbox_url, 'old_domain', 'new_domain');

More resources

  • https://stackoverflow.com/questions/24718706/backup-restore-a-dockerized-postgresql-database

Federation

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 API outline.

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 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 in_reply_to 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.

Lemmy Federation Protocol

The Lemmy Protocol (or Lemmy Federation Protocol) is a subset of the ActivityPub Protocol, with some extensions.

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.

Before reading this, have a look at our Federation Overview to get an idea how Lemmy federation works on a high level.

Context

[
  "https://www.w3.org/ns/activitystreams",
  {
    "stickied": "as:stickied",
    "pt": "https://join-lemmy.org#",
    "sc": "http://schema.org#",
    "matrixUserId": {
      "type": "sc:Text",
      "id": "as:alsoKnownAs"
    },
    "sensitive": "as:sensitive",
    "comments_enabled": {
      "type": "sc:Boolean",
      "id": "pt:commentsEnabled"
    },
    "moderators": "as:moderators"
  },
  "https://w3id.org/security/v1"
]

The context is identical for all activities and objects.

Actors

Community

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)

{
  "id": "https://enterprise.lemmy.ml/c/tenforward",
  "type": "Group",
  "preferredUsername": "main",
  "name": "Ten Forward",
  "summary": "<p>Lounge and recreation facility</p>\n<hr />\n<p>Welcome to the <a href=\"https://memory-alpha.fandom.com/wiki/USS_Enterprise_(NCC-1701-D)\">Enterprise</a>!.</p>\n",
  "source": {
    "content": "Lounge and recreation facility\n\n---\n\nWelcome to the [Enterprise](https://memory-alpha.fandom.com/wiki/USS_Enterprise_(NCC-1701-D))!.",
    "mediaType": "text/markdown"
  },
  "sensitive": false,
  "icon": {
    "type": "Image",
    "url": "https://enterprise.lemmy.ml/pictrs/image/waqyZwLAy4.webp"
  },
  "image": {
    "type": "Image",
    "url": "https://enterprise.lemmy.ml/pictrs/image/Wt8zoMcCmE.jpg"
  },
  "inbox": "https://enterprise.lemmy.ml/c/tenforward/inbox",
  "followers": "https://enterprise.lemmy.ml/c/tenforward/followers",
  "moderators": "https://enterprise.lemmy.ml/c/tenforward/moderators",
  "endpoints": {
    "sharedInbox": "https://enterprise.lemmy.ml/inbox"
  },
  "outbox": "https://enterprise.lemmy.ml/c/tenforward/outbox",
  "publicKey": {
    "id": "https://enterprise.lemmy.ml/c/tenforward#main-key",
    "owner": "https://enterprise.lemmy.ml/c/tenforward",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzRjKTNtvDCmugplwEh+g\nx1bhKm6BHUZfXfpscgMMm7tXFswSDzUQirMgfkxa9ubfr1PDFKffA2vQ9x6CyuO/\n70xTafdOHyV1tSqzgKz0ZvFZ/VCOo6qy1mYWVkrtBm/fKzM+87MdkKYB/zI4VyEJ\nLfLQgjwxBAEYUH3CBG71U0gO0TwbimWNN0vqlfp0QfThNe1WYObF88ZVzMLgFbr7\nRHBItZjlZ/d8foPDidlIR3l2dJjy0EsD8F9JM340jtX7LXqFmU4j1AQKNHTDLnUF\nwYVhzuQGNJ504l5LZkFG54XfIFT7dx2QwuuM9bSnfPv/98RYrq1Si6tCkxEt1cVe\n4wIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  "published": "2019-06-02T16:43:50.799554+00:00",
  "updated": "2021-03-10T17:18:10.498868+00:00"
}
Field NameDescription
preferredUsernameName of the actor
nameTitle of the community
sensitiveTrue indicates that all posts in the community are nsfw
attributedToFirst the community creator, then all the remaining moderators
contentText for the community sidebar, usually containing a description and rules
iconIcon, shown next to the community name
imageBanner image, shown on top of the community page
inboxActivityPub inbox URL
outboxActivityPub outbox URL, only contains up to 20 latest posts, no comments, votes or other activities
followersFollower collection URL, only contains the number of followers, no references to individual followers
endpointsContains URL of shared inbox
publishedDatetime when the community was first created
updatedDatetime when the community was last changed
publicKeyThe public key used to verify signatures from this actor

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)

{
  "id": "https://enterprise.lemmy.ml/u/picard",
  "type": "Person",
  "preferredUsername": "picard",
  "name": "Jean-Luc Picard",
  "summary": "<p>Captain of the starship <strong>Enterprise</strong>.</p>\n",
  "source": {
    "content": "Captain of the starship **Enterprise**.",
    "mediaType": "text/markdown"
  },
  "icon": {
    "type": "Image",
    "url": "https://enterprise.lemmy.ml/pictrs/image/ed9ej7.jpg"
  },
  "image": {
    "type": "Image",
    "url": "https://enterprise.lemmy.ml/pictrs/image/XenaYI5hTn.png"
  },
  "matrix_user_id": "@picard:matrix.org",
  "inbox": "https://enterprise.lemmy.ml/u/picard/inbox",
  "outbox": "https://enterprise.lemmy.ml/u/picard/outbox",
  "endpoints": {
    "sharedInbox": "https://enterprise.lemmy.ml/inbox"
  },
  "published": "2020-01-17T01:38:22.348392+00:00",
  "updated": "2021-08-13T00:11:15.941990+00:00",
  "publicKey": {
    "id": "https://enterprise.lemmy.ml/u/picard#main-key",
    "owner": "https://enterprise.lemmy.ml/u/picard",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0lP99/s5Vv+XbPdkeqIJ\nwoD4GFnHmBnBHdEKChEUWfWj1TtioC/rGNoXFQeXQA3Amhy4nxSceiDnUgwkkuQY\nv0MtIW58NzgknEavtllxL+LSds5pg3gANaDIk8UiWTkqXTg0GnlJMpCK1Chen0l/\nszL6DEvUyTSuS5ZYDXFgewF89Pe7U0S15V5U2Harv7AgJYDyxmUL0D1pGuUCRqcE\nl5MTHJjrXeNnH1w2g8aly8YlO/Cr0L51rFg/lBF23vni7ZLv8HbmWh6YpaAf1R8h\nE45zKR7OHqymdjzrg1ITBwovefpwMkVgnJ+Wdr4HPnFlBSkXPoZeM11+Z8L0anzA\nXwIDAQAB\n-----END PUBLIC KEY-----\n"
  }
}
Field NameDescription
preferredUsernameName of the actor
nameThe user's displayname
contentUser bio
iconThe user's avatar, shown next to the username
imageThe user's banner, shown on top of the profile
inboxActivityPub inbox URL
endpointsContains URL of shared inbox
publishedDatetime when the user signed up
updatedDatetime when the user profile was last changed
publicKeyThe 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.

Objects

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.

{
  "id": "https://enterprise.lemmy.ml/post/55143",
  "type": "Page",
  "attributedTo": "https://enterprise.lemmy.ml/u/picard",
  "to": [
    "https://enterprise.lemmy.ml/c/tenforward",
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "name": "Post title",
  "content": "<p>This is a post in the /c/tenforward community</p>\n",
  "mediaType": "text/html",
  "source": {
    "content": "This is a post in the /c/tenforward community",
    "mediaType": "text/markdown"
  },
  "url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png",
  "image": {
    "type": "Image",
    "url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png"
  },
  "sensitive": false,
  "commentsEnabled": true,
  "stickied": true,
  "published": "2021-02-26T12:35:34.292626+00:00"
}
Field NameDescription
attributedToID of the user which created this post
toID of the community where it was posted to
nameTitle of the post
contentBody of the post
urlAn arbitrary link to be shared
imageThumbnail for url, only present if it is an image link
commentsEnabledFalse indicates that the post is locked, and no comments can be added
sensitiveTrue marks the post as NSFW, blurs the thumbnail and hides it from users with NSFW settign disabled
stickiedTrue means that it is shown on top of the community
publishedDatetime when the post was created
updatedDatetime when the post was edited (not present if it was never edited)

Comment

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.

{
  "id": "https://enterprise.lemmy.ml/comment/38741",
  "type": "Note",
  "attributedTo": "https://enterprise.lemmy.ml/u/picard",
  "to": ["https://www.w3.org/ns/activitystreams#Public"],
  "cc": [
    "https://enterprise.lemmy.ml/c/tenforward",
    "https://enterprise.lemmy.ml/u/picard"
  ],
  "inReplyTo": "https://enterprise.lemmy.ml/post/55143",
  "content": "<p>first comment!</p>\n",
  "mediaType": "text/html",
  "source": {
    "content": "first comment!",
    "mediaType": "text/markdown"
  },
  "tag": [
    {
      "href": "https://enterprise.lemmy.ml/u/picard",
      "type": "Mention",
      "name": "@picard@enterprise.lemmy.ml"
    }
  ],
  "published": "2021-03-01T13:42:43.966208+00:00",
  "updated": "2021-03-01T13:43:03.955787+00:00"
}
Field NameDescription
attributedToID of the user who created the comment
toCommunity where the comment was made
contentThe comment text
inReplyToIDs of the post where this comment was made, and the parent comment. If this is a top-level comment, inReplyTo only contains the post
publishedDatetime when the comment was created
updatedDatetime when the comment was edited (not present if it was never edited)

Private Message

A direct message from one user to another. Can not include additional users. Threading is not implemented yet, so the inReplyTo field is missing.

{
  "id": "https://enterprise.lemmy.ml/private_message/1621",
  "type": "ChatMessage",
  "attributedTo": "https://enterprise.lemmy.ml/u/picard",
  "to": [
    "https://queer.hacktivis.me/users/lanodan"
  ],
  "content": "<p>Hello hello, testing</p>\n",
  "mediaType": "text/html",
  "source": {
    "content": "Hello hello, testing",
    "mediaType": "text/markdown"
  },
  "published": "2021-10-21T10:13:14.597721+00:00"
}
Field NameDescription
attributedToID of the user who created this private message
toID of the recipient
contentThe text of the private message
publishedDatetime when the message was created
updatedDatetime when the message was edited (not present if it was never edited)

Collections

Community Outbox

{
  "type": "OrderedCollection",
  "id": "https://ds9.lemmy.ml/c/main/outbox",
  "totalItems": 7,
  "orderedItems": [
    {
      "actor": "https://ds9.lemmy.ml/u/dess_ds9",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1685",
        "attributedTo": "https://ds9.lemmy.ml/u/dess_ds9",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "Test post",
        "mediaType": "text/html",
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-09-30T16:37:58.425718+00:00",
        "updated": "2021-09-30T16:39:50.934055+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/157bc329-05cb-4dc3-ad9e-5110fde3f3aa"
    },
    {
      "actor": "https://ds9.lemmy.ml/u/nutomic",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1665",
        "attributedTo": "https://ds9.lemmy.ml/u/nutomic",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "another webmention test",
        "mediaType": "text/html",
        "url": "https://webmention.rocks/test/1",
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-09-17T13:22:15.026912+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/c54e4509-16ac-42bf-b3b4-0bf8516f8152"
    },
    {
      "actor": "https://ds9.lemmy.ml/u/nutomic",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1664",
        "attributedTo": "https://ds9.lemmy.ml/u/nutomic",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "another test",
        "mediaType": "text/html",
        "url": "https://webmention.rocks/test/1",
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-09-17T13:13:21.675891+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/25f7d2cb-11d5-4c9c-aa3c-85fbff9f9e0c"
    },
    {
      "actor": "https://ds9.lemmy.ml/u/nutomic",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1663",
        "attributedTo": "https://ds9.lemmy.ml/u/nutomic",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "Webmention test from Lemmy",
        "mediaType": "text/html",
        "url": "https://webmention.rocks/test/1",
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-09-17T13:00:15.392844+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/cfbd12b8-2e11-42b6-a609-b482decbaf11"
    },
    {
      "actor": "https://ds9.lemmy.ml/u/dess_tester_3",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1644",
        "attributedTo": "https://ds9.lemmy.ml/u/dess_tester_3",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "The best wireless earbuds you can buy right now | Engadget",
        "mediaType": "text/html",
        "url": "https://www.engadget.com/best-wireless-earbuds-120058222.html",
        "image": {
          "type": "Image",
          "url": "https://ds9.lemmy.ml/pictrs/image/0WWsYOuwAE.jpg"
        },
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-08-26T01:22:06.428368+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/76c94408-944a-4a2f-a88b-d10f12b472b0"
    },
    {
      "actor": "https://ds9.lemmy.ml/u/dess_ds9",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1643",
        "attributedTo": "https://ds9.lemmy.ml/u/dess_ds9",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "First Look: Cadillac’s luxury EV debut seems like a winner | Engadges",
        "content": "<p>test</p>\n",
        "mediaType": "text/html",
        "source": {
          "content": "test",
          "mediaType": "text/markdown"
        },
        "url": "https://www.engadget.com/cadillac-lyriq-luxury-ev-first-look-video-171543752.html",
        "image": {
          "type": "Image",
          "url": "https://ds9.lemmy.ml/pictrs/image/gnmtvgXP31.jpg"
        },
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-08-23T23:43:06.560543+00:00",
        "updated": "2021-08-23T23:52:51.832606+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/b1f95918-f593-4951-91cf-2c3340cd9509"
    },
    {
      "actor": "https://ds9.lemmy.ml/u/dess_ds9_2",
      "to": [
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "object": {
        "type": "Page",
        "id": "https://ds9.lemmy.ml/post/1642",
        "attributedTo": "https://ds9.lemmy.ml/u/dess_ds9_2",
        "to": [
          "https://ds9.lemmy.ml/c/main",
          "https://www.w3.org/ns/activitystreams#Public"
        ],
        "name": "A test post from DS9",
        "mediaType": "text/html",
        "commentsEnabled": true,
        "sensitive": false,
        "stickied": false,
        "published": "2021-08-06T14:10:47.493075+00:00"
      },
      "cc": [
        "https://ds9.lemmy.ml/c/main"
      ],
      "type": "Create",
      "id": "https://ds9.lemmy.ml/activities/create/6359b2e7-badb-4241-b5ee-b093078361bd"
    }
  ]
}

The outbox only contains Create/Post activities for now.

Community Followers

{
  "id": "http://enterprise.lemmy.ml/c/main/followers",
  "type": "Collection",
  "totalItems": 3,
  "items": []
}

The followers collection is only used to expose the number of followers. Actor IDs are not included, to protect user privacy.

Community Moderators

{
  "type": "OrderedCollection",
  "id": "https://enterprise.lemmy.ml/c/tenforward/moderators",
  "orderedItems": [
    "https://enterprise.lemmy.ml/u/picard"
  ]
}

User Outbox

{
  "type": "OrderedCollection",
  "id": "http://ds9.lemmy.ml/u/lemmy_alpha/outbox",
  "orderedItems": [],
  "totalItems": 0
}

Activities

User to Community

Follow

When the user clicks "Subscribe" in a community, a Follow is sent. The community automatically responds with an Accept/Follow.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "object": "http://enterprise.lemmy.ml/c/main",
  "type": "Follow",
  "id": "http://ds9.lemmy.ml/activities/follow/6abcd50b-b8ca-4952-86b0-a6dd8cc12866"
}

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.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "object": {
    "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
    "object": "http://enterprise.lemmy.ml/c/main",
    "type": "Follow",
    "id": "http://ds9.lemmy.ml/activities/follow/dc2f1bc5-f3a0-4daa-a46b-428cbfbd023c"
  },
  "type": "Undo",
  "id": "http://ds9.lemmy.ml/activities/undo/dd83c482-8ebd-4b6c-9008-c8373bd1a86a"
}

Report Post or Comment

Reports a post or comment for rule violation, so that mods/admins review it.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "to": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "object": "http://enterprise.lemmy.ml/post/7",
  "summary": "report this post",
  "type": "Flag",
  "id": "http://ds9.lemmy.ml/activities/flag/98b0933f-5e45-4a95-a15f-e0dc86361ba4"
}

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.

{
  "actor": "http://enterprise.lemmy.ml/c/main",
  "object": {
    "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
    "object": "http://enterprise.lemmy.ml/c/main",
    "type": "Follow",
    "id": "http://ds9.lemmy.ml/activities/follow/6abcd50b-b8ca-4952-86b0-a6dd8cc12866"
  },
  "type": "Accept",
  "id": "http://enterprise.lemmy.ml/activities/accept/75f080cc-3d45-4654-8186-8f3bb853fa27"
}

Announce

When the community receives a post or comment activity, it wraps that into an Announce and sends it to all followers.

{
  "actor": "http://enterprise.lemmy.ml/c/main",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "object": {
      "type": "Page",
      "id": "http://enterprise.lemmy.ml/post/7",
      "attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta",
      "to": [
        "http://enterprise.lemmy.ml/c/main",
        "https://www.w3.org/ns/activitystreams#Public"
      ],
      "name": "post 4",
      "mediaType": "text/html",
      "commentsEnabled": true,
      "sensitive": false,
      "stickied": false,
      "published": "2021-11-01T12:11:22.871846+00:00"
    },
    "cc": [
      "http://enterprise.lemmy.ml/c/main"
    ],
    "type": "Create",
    "id": "http://enterprise.lemmy.ml/activities/create/2807c9ec-3ad8-4859-a9e0-28b59b6e499f"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main/followers"
  ],
  "type": "Announce",
  "id": "http://enterprise.lemmy.ml/activities/announce/8030b171-803a-4108-94b1-342688f375cf"
}

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.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "type": "Page",
    "id": "http://ds9.lemmy.ml/post/1",
    "attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha",
    "to": [
      "http://enterprise.lemmy.ml/c/main",
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "name": "test post",
    "content": "<p>test body</p>\n",
    "mediaType": "text/html",
    "source": {
      "content": "test body",
      "mediaType": "text/markdown"
    },
    "url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg",
    "commentsEnabled": true,
    "sensitive": false,
    "stickied": false,
    "published": "2021-10-29T15:10:51.557399+00:00"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Create",
  "id": "http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf"
}

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.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "type": "Note",
    "id": "http://ds9.lemmy.ml/comment/1",
    "attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha",
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "content": "hello",
    "mediaType": "text/html",
    "source": {
      "content": "hello",
      "mediaType": "text/markdown"
    },
    "inReplyTo": "http://ds9.lemmy.ml/post/1",
    "published": "2021-11-01T11:45:49.794920+00:00"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main",
    "http://ds9.lemmy.ml/u/lemmy_alpha"
  ],
  "tag": [
    {
      "href": "http://ds9.lemmy.ml/u/lemmy_alpha",
      "type": "Mention",
      "name": "@lemmy_alpha@ds9.lemmy.ml"
    }
  ],
  "type": "Create",
  "id": "http://ds9.lemmy.ml/activities/create/1e77d67c-44ac-45ed-bf2a-460e21f60236"
}

Like Post or Comment

An upvote for a post or comment.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": "http://ds9.lemmy.ml/comment/1",
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Like",
  "id": "http://ds9.lemmy.ml/activities/like/fd61d070-7382-46a9-b2b7-6bb253732877"
}

Dislike Post or Comment

A downvote for a post or comment.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": "http://ds9.lemmy.ml/post/1",
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Dislike",
  "id": "http://enterprise.lemmy.ml/activities/dislike/64d40d40-a829-43a5-8247-1fb595b3ca1c"
}

Undo Like or Dislike Post or Comment

Remove a vote that was previously done by the same user.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "object": "http://ds9.lemmy.ml/comment/1",
    "cc": [
      "http://enterprise.lemmy.ml/c/main"
    ],
    "type": "Like",
    "id": "http://ds9.lemmy.ml/activities/like/efcf7ae2-dfcc-4ff4-9ce4-6adf251ff004"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Undo",
  "id": "http://ds9.lemmy.ml/activities/undo/3518565c-24a7-4d9e-8e0a-f7a2f45ac618"
}

Delete Post or Comment

Deletes a previously created post or comment. This can only be done by the original creator of that post/comment.

{
  "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "id": "http://ds9.lemmy.ml/post/1",
    "type": "Tombstone"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Delete",
  "id": "http://ds9.lemmy.ml/activities/delete/f2abee48-c7bb-41d5-9e27-8775ff32db12"
}

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.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "id": "http://ds9.lemmy.ml/comment/1",
    "type": "Tombstone"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Delete",
  "summary": "bad comment",
  "id": "http://enterprise.lemmy.ml/activities/delete/42ca1a79-f99e-4518-a2ca-ba2df221eb5e"
}

Undo Delete or Remove

Reverts the action done by the activity in the object field. In this example, the removed Note is restored.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": {
    "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "object": {
      "id": "http://ds9.lemmy.ml/comment/1",
      "type": "Tombstone"
    },
    "cc": [
      "http://enterprise.lemmy.ml/c/main"
    ],
    "type": "Delete",
    "summary": "bad comment",
    "id": "http://enterprise.lemmy.ml/activities/delete/2598435c-87a3-49cd-81f3-a44b03b7af9d"
  },
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Undo",
  "id": "http://enterprise.lemmy.ml/activities/undo/a850cf21-3866-4b3a-b80b-56aa00997fee"
}

Add 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.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "target": "http://enterprise.lemmy.ml/c/main/moderators",
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Add",
  "id": "http://enterprise.lemmy.ml/activities/add/ec069147-77c3-447f-88c8-0ef1df10403f"
}

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.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "type": "Remove",
  "target": "http://enterprise.lemmy.ml/c/main/moderators",
  "id": "http://enterprise.lemmy.ml/activities/remove/aab114f8-cfbd-4935-a5b7-e1a64603650d"
}

Block User

Blocks a user from a community, so he can't participate in it.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "target": "http://enterprise.lemmy.ml/c/main",
  "type": "Block",
  "id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2"
}

Undo Block User

Reverts a previous user block.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "object": "http://ds9.lemmy.ml/u/lemmy_alpha",
  "cc": [
    "http://enterprise.lemmy.ml/c/main"
  ],
  "target": "http://enterprise.lemmy.ml/c/main",
  "type": "Block",
  "id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2"
}

User to User

Create or Update Private message

Creates a new private message between two users.

{
  "id": "http://enterprise.lemmy.ml/activities/create/987d05fa-f637-46d7-85be-13d112bc269f",
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "http://ds9.lemmy.ml/u/lemmy_alpha"
  ],
  "object": {
    "type": "ChatMessage",
    "id": "http://enterprise.lemmy.ml/private_message/1",
    "attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta",
    "to": [
      "http://ds9.lemmy.ml/u/lemmy_alpha"
    ],
    "content": "hello",
    "mediaType": "text/html",
    "source": {
      "content": "hello",
      "mediaType": "text/markdown"
    },
    "published": "2021-10-29T15:31:56.058289+00:00"
  },
  "type": "Create"
}

Delete Private Message

Deletes a previous private message.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "http://enterprise.lemmy.ml/u/lemmy_beta"
  ],
  "object": "http://enterprise.lemmy.ml/private_message/1",
  "type": "Delete",
  "id": "http://enterprise.lemmy.ml/activities/delete/041d9858-5eef-4ad9-84ae-7455b4d87ed9"
}

Undo Delete Private Message

Restores a previously deleted private message. The object is regenerated from scratch, as such the activity ID and other fields are different.

{
  "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
  "to": [
    "http://ds9.lemmy.ml/u/lemmy_alpha"
  ],
  "object": {
    "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
    "to": [
      "http://enterprise.lemmy.ml/u/lemmy_beta"
    ],
    "object": "http://enterprise.lemmy.ml/private_message/1",
    "type": "Delete",
    "id": "http://enterprise.lemmy.ml/activities/delete/616c41be-04ed-4bd4-b865-30712186b122"
  },
  "type": "Undo",
  "id": "http://enterprise.lemmy.ml/activities/undo/35e5b337-014c-4bbe-8d63-6fac96f51409"
}

Client Development

Lemmy HTTP API Extras

This contains extras not in the API docs.

Curl Examples

GET example

curl "http://localhost:8536/api/v2/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/v2/comment/like

HTTP API exclusive features

These features cannot be accessed from the WebSocket API:

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}

Theming Guide

Lemmy uses Bootstrap v4, and very few custom css classes, so any bootstrap v4 compatible theme should work fine.

Creating

  • Use a tool like bootstrap.build to create a bootstrap v4 theme. Export the bootstrap.min.css once you're done, and save the _variables.scss too.

Testing

  • To test out a theme, you can either use your browser's web tools, or a plugin like stylus to copy-paste a theme, when viewing Lemmy.

Adding

  1. Fork the lemmy-ui.
  2. Copy {my-theme-name}.min.css to src/assets/css/themes. (You can also copy the _variables.scss here if you want).
  3. Go to src/shared/utils.ts and add {my-theme-name} to the themes list.
  4. Test locally
  5. Do a pull request with those changes.

Creating a Custom Frontend

The backend and frontend are completely decoupled, and run in independent Docker containers. They only communicate over the Lemmy API, 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, or a question-and-answer site like stackoverflow. 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, lemmy-lite, or the lemmy-frontend-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, 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). 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 repository to your project as a git submodule. 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:

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);

Contributing

Information about contributing to Lemmy, whether it is translating, testing, designing or programming.

Issue tracking / Repositories

Translating

Check out Lemmy's Weblate for translations. You can also help by translating this documentation.

Architecture

Front end

  • The front end is written in typescript, using a react-like framework called inferno. All UI elements are reusable .tsx components.
  • The front end repository is 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.

Docker Development

Dependencies

Debian-based distro

sudo apt install git docker-compose
sudo systemctl start docker
git clone https://github.com/LemmyNet/lemmy

Arch-based distro

sudo pacman -S git docker-compose
sudo systemctl start docker
git clone https://github.com/LemmyNet/lemmy

Running

cd docker/dev
./docker_update.sh

and go to http://localhost:1235.

*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 instead.

Local Development

Install build requirements

Install Rust using the recommended option on rust-lang.org (rustup).

Debian-based distro

sudo apt install git cargo libssl-dev pkg-config libpq-dev yarn curl gnupg2 espeak
# 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 yarn curl gnupg2 espeak
# install yarn (stable)
curl -o- -L https://yarnpkg.com/install.sh | bash

macOS

Install Homebrew 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
# or alternatively from gitea
# git clone https://yerbamate.ml/LemmyNet/lemmy.git

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 --recurse-submodules

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 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. 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.

Tests

Rust

After installing local development dependencies, run the following commands:

psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
./test.sh

Federation

Install the Local development dependencies, 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

Federation Development

Running locally

Install the dependencies as described in Docker development. Then run the following

cd docker/federation
./start-local-instances.bash

The federation test sets up 5 instances:

InstanceUsernameLocationNotes
lemmy-alphalemmy_alpha127.0.0.1:8540federated with all other instances
lemmy-betalemmy_beta127.0.0.1:8550federated with all other instances
lemmy-gammalemmy_gamma127.0.0.1:8560federated with all other instances
lemmy-deltalemmy_delta127.0.0.1:8570only allows federation with lemmy-beta
lemmy-epsilonlemmy_epsilon127.0.0.1:8580uses 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 or manually. Then replace the line image: dessalines/lemmy:v0.x.x in /lemmy/docker-compose.yml with image: dessalines/lemmy:federation. Add and configure this federation block to your lemmy.hjson.

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.

Branching and Releases

Branches

In general, our handling of branches is the one described in 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

Resources / Libraries

ActivityPub Resources

Official Documents

Explanations

Examples and Libraries

Code of Conduct

  • 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.
  • Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.
  • Please be kind and courteous. There’s no need to be mean or rude.
  • 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.
  • 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.
  • 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; 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.
  • 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.
  • Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.

Message the Moderation Team on Mastodon

Email The Moderation Team

Moderation

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 .

  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.)
  2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
  3. Moderators will first respond to such remarks with a warning, at the same time the offending content will likely be removed whenever possible.
  4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.
  5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
  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.
  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.
  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.

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.

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.

The enforcement policies listed above apply to all official Lemmy venues; including git repositories under github.com/LemmyNet and yerbamate.ml/LemmyNet, the Matrix channel; 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.

Adapted from the Rust Code of Conduct, which is based on the Node.js Policy on Trolling as well as the Contributor Covenant v1.3.0.