Vibefed kb-lemmy
git clone https://github.com/reiver/vibefed
T=$(mktemp -d) && git clone --depth=1 https://github.com/reiver/vibefed "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/kb-lemmy" ~/.claude/skills/reiver-vibefed-kb-lemmy && rm -rf "$T"
skills/kb-lemmy/SKILL.mdLemmy — Complete Reference
Overview
Lemmy is a self-hosted, federated social link aggregation and discussion forum — the Fediverse equivalent of Reddit. Created by Dessalines in February 2019, with Nutomic as co-creator and primary federation implementer. Built in Rust (Actix-web, Diesel ORM, PostgreSQL) with a TypeScript/Inferno.js frontend. Licensed AGPL-3.0.
Lemmy federates via ActivityPub. Its fundamental design choice is representing communities as Group actors that automatically relay all content via
Announce activities, ensuring cross-instance consistency.
It is the largest platform in the "Threadiverse" — the threaded discussion
subset of the Fediverse.
1. Technology Stack
| Component | Technology |
|---|---|
| Backend language | Rust |
| Web framework | Actix-web (actor-model based) |
| ORM | Diesel (type-safe) |
| Database | PostgreSQL (R2D2 connection pooling) |
| Frontend language | TypeScript + CSS |
| Frontend framework | Inferno.js (React-compatible) |
| API client | lemmy-js-client |
| Image service | pictrs |
| Deployment | Docker, Ansible, ARM64/RPi supported |
| License | AGPL-3.0 |
| Federation library | activitypub-federation-rust (standalone reusable crate) |
2. ActivityPub Object Type Mapping
| Lemmy Concept | ActivityPub Type | Notes |
|---|---|---|
| Community | | Automated relay actor; Announces all content to followers |
| User | | Creates content, moderates, sends PMs |
| Instance | | Instance-level actor at root path |
| Post | | Mandatory title (), optional URL and body |
| Comment | | Tree structure via |
| Private Message | | Direct user-to-user, no threading |
Why Page for Posts?
Lemmy posts have a mandatory title (
name), distinguishing them from
Note (typically untitled) and Article (long-form). The Page type
aligns with the concept of a web page with a URL, title, and optional
body.
3. Communities as Group Actors
This is the fundamental architectural decision. Communities are
Group
actors with their own inboxes. Every post, comment, vote, and moderation
action is received by the community and then Announced (wrapped and
forwarded) to all followers across all instances.
Community Object
{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://join-lemmy.org/context.json" ], "id": "https://enterprise.lemmy.ml/c/main", "type": "Group", "preferredUsername": "main", "name": "The Main Community", "summary": "<p>Welcome to the main community!</p>", "inbox": "https://enterprise.lemmy.ml/c/main/inbox", "outbox": "https://enterprise.lemmy.ml/c/main/outbox", "followers": "https://enterprise.lemmy.ml/c/main/followers", "featured": "https://enterprise.lemmy.ml/c/main/featured", "attributedTo": ["https://enterprise.lemmy.ml/u/picard"], "publicKey": { "id": "https://enterprise.lemmy.ml/c/main#main-key", "owner": "https://enterprise.lemmy.ml/c/main", "publicKeyPem": "..." } }
Key Fields
| Field | Description |
|---|---|
| Machine-readable community name |
| Human-readable display name |
| Description / rules (HTML) |
/ | Avatar / banner |
| Limited to 20 latest posts |
| Count only (no individual references — privacy) |
| Creators and moderators |
| Pinned posts collection |
| Whether only mods can post |
The Announce Relay Pattern
User on Instance A Community on Instance B Instance C subscriber | | | |--- Create{Page} -----------> | | | |--- Announce{Create{Page}} ->| | | |
All content activities (Create, Update, Delete, Like, Dislike, Remove, Lock) flow through the community Group actor, which wraps each in an
Announce and broadcasts to all followers. This is how cross-instance
consistency is maintained.
4. Post (Page) Object
{ "type": "Page", "id": "https://enterprise.lemmy.ml/post/123", "attributedTo": "https://enterprise.lemmy.ml/u/riker", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://ds9.lemmy.ml/c/main" ], "audience": "https://ds9.lemmy.ml/c/main", "name": "An interesting link", "content": "<p>Check this out!</p>", "source": { "content": "Check this out!", "mediaType": "text/markdown" }, "attachment": [], "image": {}, "commentsEnabled": true, "sensitive": false, "stickied": false, "published": "2024-01-15T12:00:00.000Z" }
| Field | Description |
|---|---|
| Title (mandatory — distinguishes Post from Comment) |
| Body text (HTML-rendered) |
| Original markdown with |
| Target community (for routing) |
| Links/images array |
| Thumbnail |
| Post lock status (custom extension) |
| NSFW flag |
| Pinned status (custom extension) |
| Language metadata ( + ) |
5. Comment (Note) Object
{ "type": "Note", "id": "https://enterprise.lemmy.ml/comment/95", "attributedTo": "https://enterprise.lemmy.ml/u/picard", "to": "https://enterprise.lemmy.ml/c/main", "content": "I agree with this take.", "source": { "content": "I agree with this take.", "mediaType": "text/markdown" }, "inReplyTo": [ "https://enterprise.lemmy.ml/post/38", "https://voyager.lemmy.ml/comment/73" ], "tag": [ { "type": "Mention", "href": "https://voyager.lemmy.ml/u/janeway", "name": "@janeway@voyager.lemmy.ml" } ], "distinguished": false, "published": "2024-01-15T13:00:00.000Z" }
| Field | Description |
|---|---|
| Parent post and/or parent comment (builds tree) |
| objects for user references |
| Mod-highlighted comment (custom extension) |
6. Voting — Like and Dislike
Lemmy uses both
Like (upvote) and Dislike (downvote) as first-class
ActivityPub activities. This is unique among major Fediverse platforms
— most only support Like.
Upvote
{ "type": "Like", "actor": "https://enterprise.lemmy.ml/u/picard", "object": "https://enterprise.lemmy.ml/post/123", "audience": "https://ds9.lemmy.ml/c/main" }
Downvote
{ "type": "Dislike", "actor": "https://enterprise.lemmy.ml/u/worf", "object": "https://enterprise.lemmy.ml/post/123", "audience": "https://ds9.lemmy.ml/c/main" }
Undo Vote
{ "type": "Undo", "actor": "https://enterprise.lemmy.ml/u/picard", "object": { "type": "Like", "actor": "https://enterprise.lemmy.ml/u/picard", "object": "https://enterprise.lemmy.ml/post/123" } }
All votes include
audience for community routing and are wrapped in
Announce by the community actor.
Privacy note: Every instance that subscribes to the community receives all vote activities — instance admins can see who voted on what.
Ranking Algorithms
| Algorithm | Description |
|---|---|
| Active (default) | Score + latest comment time with decay |
| Hot | Score + post publication time with decay |
| Scaled | Hot with boost for less active communities |
| New / Old | Chronological |
| Most Comments | Comment count |
| New Comments | Forum-style bump on new reply |
| Top (Day/Week/Month/Year/All) | Score within time window |
Score = upvotes minus downvotes.
7. Full Activity Reference
User → Community
| Activity | Purpose |
|---|---|
| Subscribe to community |
| Unsubscribe |
| New post |
| New comment |
| Edit post |
| Edit comment |
| Upvote |
| Downvote |
/ | Remove vote |
| Delete own content |
| Restore deleted content |
| Report content (with reason) |
Community → Followers
| Activity | Purpose |
|---|---|
| Automatic response to Follow |
| Rebroadcast any received activity to all followers |
Moderation
| Activity | Purpose |
|---|---|
| Remove content (with reason) |
| Ban user (with scope, optional , , ) |
| Prevent new comments on post |
to | Add moderator |
from | Remove moderator |
to | Pin post |
from | Unpin post |
User → User
| Activity | Purpose |
|---|---|
| Send private message |
| Edit private message |
| Delete private message |
Instance-Level
- Instance actor (type
) at root pathApplication - Admins can remove any content instance-wide
on user actor withDelete
for account deletionremoveData: true
All activities are signed with RSA keys (key ID:
{actor-url}#main-key).
8. JSON-LD Context and Custom Extensions
Required Context
[ "https://join-lemmy.org/context.json", "https://www.w3.org/ns/activitystreams" ]
Namespace Prefixes
{ "lemmy": "https://join-lemmy.org/ns#", "litepub": "http://litepub.social/ns#", "pt": "https://joinpeertube.org/ns#", "sc": "http://schema.org/" }
Custom Properties
| Property | Namespace | Used on | Description |
|---|---|---|---|
| lemmy | Page | Pinned post |
| lemmy | Note | Mod-highlighted comment |
| lemmy | Page | Post lock status |
| lemmy | Group | Restricted posting |
| lemmy | Group | Moderator collection |
| lemmy | Page, Note | Language metadata |
| as | Page | NSFW flag |
| lemmy | Person | Matrix chat ID |
Important: Lemmy parses ActivityPub objects as plain JSON, not as JSON-LD. The
@context is included for other software's benefit but
Lemmy does not perform JSON-LD expansion or compaction.
9. Federation Modes and Fetching
Federation Modes
| Mode | Description |
|---|---|
| Allowlist | Only connect to specified instances |
| Blocklist | Block specific instances, open to all others |
| Open (default) | Connect to any instance |
Cascading Fetch Behavior
- Community fetch: Also fetches most recent posts (not comments/votes)
- Post fetch: Also fetches community and author
- Comment fetch: Also fetches all parent comments, the post, authors, and the community
This ensures referential integrity across the federated network.
10. REST API
| Aspect | Details |
|---|---|
| Version | v3 () |
| Auth | JWT (cookies or header since 0.19.0) |
| Image service | pictrs — upload, resize, format conversion |
| Feeds | RSS/Atom at paths |
| Rate limiting | IP-based (Docker: requires header) |
| Client library | lemmy-js-client (official TypeScript) |
| OpenAPI spec | Unofficial: |
11. Interoperability Issues
name vs preferredUsername (Historical)
Lemmy initially reversed these:
name for usernames, preferredUsername
for display names. This is the opposite of the ActivityPub spec and
Mastodon. Nutomic acknowledged it as "completely counterintuitive."
JSON-LD Context Errors
Lemmy's
@context had incorrect declarations:
— falsely implied"stickied": "as:stickied"
is in the ActivityStreams namespacestickied
prefix expanded to Lemmy's domain instead of PeerTube's"pt"
Nutomic admitted: "the truth is that I don't really understand how [@context] works."
Mastodon Reply Propagation
Replies from Mastodon can fail to reach all Lemmy instances because Mastodon doesn't include the community inbox in its recipient lists. The community Group actor must be in the recipient list to Announce the reply.
Mastodon Vote Federation
Mastodon's
Like targets personal inboxes, not shared inboxes. Lemmy
processes the vote locally but does not Announce it, causing vote count
inconsistencies across instances.
Strict Parsing
Lemmy is "really inflexible when it comes to incoming activities." Arrays vs single values, or object IDs vs full inlined objects, can cause parsing errors. The Lemmy protocol is described as "a strict subset of the ActivityPub Protocol."
12. The Threadiverse Ecosystem
"Threadiverse" refers to Reddit-style threaded discussion platforms in the Fediverse. All federate with each other via ActivityPub.
| Platform | Language | Terminology | Notes |
|---|---|---|---|
| Lemmy | Rust | Communities | Largest, most mature |
| Mbin | PHP (fork of Kbin) | Magazines | Also supports microblogs (Mastodon-style) |
| PieFed | Python | Topics | Rapid development, approaching feature-parity |
| Sublinks | Java | — | Drop-in Lemmy replacement (alpha) |
Users on any Threadiverse platform can see and interact with content from any other, including subscribing to Lemmy communities from Mbin or PieFed.
13. Known Quirks for Implementers
-
Posts use
, notPage
— if your server sendsNote
objects to a Lemmy community, they will be treated as comments, not posts. Posts requireNote
type with aPage
(title).name -
Community must be in recipients — activities targeting a Lemmy community must include the community in
orto
. Sending only to the post author's inbox will not distribute the activity.cc -
field — Lemmy uses theaudience
property to route activities to the correct community. Include it for reliable delivery.audience -
is real — unlike most Fediverse platforms, Lemmy usesDislike
as a distinct activity type for downvotes, notDislike
.Undo{Like} -
with markdown — Lemmy preserves original markdown in thesource
field withsource
. ThemediaType: "text/markdown"
field contains the HTML-rendered version.content -
Outbox is limited — community outbox only contains the 20 most recent posts. Comments and votes are not included.
-
Followers collection is opaque — returns only a total count, not individual follower references (for privacy).
-
No JSON-LD processing — Lemmy parses JSON directly. Do not rely on JSON-LD compaction/expansion when interoperating with Lemmy.
-
for PMs — Lemmy uses the non-standardChatMessage
type for private messages rather thanChatMessage
with direct addressing.Note