Features
Everything littleFedi does
One server, one binary. This page is the long version: what is in it, how it behaves, and where the edges are. Anything listed as missing at the bottom of the overview is missing here too.
Timelines
Home, local, federated, list, tag, direct and bubble. All of them are in time order, newest first, and they stay that way. No score decides what you see, nothing is inserted, nothing is hidden because an algorithm judged it dull.
Blocks, mutes and filters are applied when a timeline is read, so a filter you change now applies to the page you load next, everywhere, without a rebuild.
Long posts fold in timelines, search results, profiles, catch-up and thread replies: the body is clamped, the cut edge fades, and a Show more control opens it. The fold is CSS, not server-side truncation, so the whole text is always in the page. A reader without stylesheets or scripting gets everything. The post a thread is focused on is never folded.
Signed-out visitors land on the local timeline. Set
public_local_timeline = false to send them to the sign-in page
instead.
Catch-up
Chronological feeds have one real weakness: come back after three days and you
scroll for twenty minutes. Catch-up is the answer to that, and it is deliberately
a separate page at /catchup that you have to open yourself. It never
leaks into a timeline.
It ranks posts your home feed already received, using signals this instance already stores:
| Signal | Weight |
|---|---|
| Each account you follow who boosted the post | +2.5 |
| Favourites delivered here (log) | +1.0 |
| Boosts delivered here (log) | +0.8 |
| Replies delivered here (log) | +0.6 |
| You interacted with the author in the last month | +1.2 |
| Carries a hashtag you follow | +1.0 |
| Continues a conversation you took part in | +0.8 |
| The post is a reply | -1.0 |
The total halves every twelve hours since the post reached your feed. The interaction counts are this instance's own view, the activities actually delivered to it, not a popularity figure fetched from somewhere else. No remote service is consulted and no ranking data leaves the machine.
Rules that are not weights:
- Your own posts are excluded, and so is anything you already favourited, bookmarked, boosted or replied to.
- A post with no displayable reason is not listed at all. If the page cannot tell you why something is there, it does not show it.
- At most two posts per author survive.
- Twenty entries, then the page ends. No pagination, no infinite scroll.
The only thing stored is a catchup marker, written when you press
Mark as read, and removed with the account like every other
marker. Pressing it moves a divider rather than emptying the page: older entries
stay listed under Already seen. Nothing about what you open, or
how long you look at it, is recorded or learned from.
Set catchup = false under [web] and the page and its
navigation entries disappear.
Posting and the composer
Posts, replies, boosts, favourites, bookmarks, polls, quotes, filters, scheduled posts, and self-expiring posts that delete themselves around a per-post timer.
The composer has three modes: plain text, Markdown, and a visual editor. The
visual mode publishes Markdown underneath, so a post keeps an editable source
and federates with the same text/markdown media type as one written
by hand. Its toolbar covers headings, bold, italic, strikethrough, links,
quotes, lists, inline and block code, horizontal rules, undo and redo. Tables
stay available from Markdown source.
Preview uses the server's real renderer, not a second implementation that drifts from it. It shows the authored post with content warnings, selected media, sensitivity, polls and quotes. Local files are previewed in the browser and are not uploaded until you actually submit.
The textarea, the Markdown checkbox and ordinary form submission keep working. The visual editor is an addition to the composer, never a replacement for it.
Federation and cryptography
ActivityPub with WebFinger discovery, shared inboxes, relays, and a durable delivery queue that retries with backoff and survives a restart. Delivery is not best effort in memory: a job that has not been acknowledged is still there tomorrow.
- HTTP Signatures
- Outbound RSA (PKCS#1 v1.5) for compatibility with everything already
deployed. Inbound RSA (
rsa-sha256,rsa-sha512,hs2019) and Ed25519 (hs2019,ed25519). Verification uses the exact advertised key ID. - Actor keys
- New local accounts carry an Ed25519 pair alongside their RSA pair. Legacy
RSA stays in
publicKey; Ed25519 is advertised separately as anassertionMethodMultikey with the required0xed01multicodec. InboundpublicKeyBase64and Multikey encodings are normalized to PKIX PEM on ingest. - Object Integrity Proofs (FEP-8b32)
eddsa-jcs-2022verification of inbound activities, always on, and accepted even when the HTTP Signature is absent or fails. Outbound signing is behindfederation.sign_with_oipand off by default. Verified against the official FEP-8b32 test vector.- Legacy Linked Data Signatures
RsaSignature2017is still verified as a fallback for forwarded activities.
Peer and client compatibility is recorded rather than claimed. The compatibility matrix is a ledger with pending rows, and a pending row is a release blocker, not an implied promise.
Accounts and sign-up
Single-user and multi-user instances from the same binary. Which one you get is
a decision littlefedi init asks about, not a different build.
- Invitations, manual approval, verified email, and TOTP multi-factor authentication with recovery codes.
require_admin_mfarefuses to let an administrator work without a second factor.- Lists, follow requests, hashtag follows, featured tags, and per-account preferences.
- Web Push notifications, plus opt-in administrator email alerts for new accounts and for signups that used a specific administrator's invite.
Registration is closed by default. That is the correct default for a server nobody is moderating yet.
Moderation and administration
Everything an operator needs is in the admin console, and almost all of it is also a CLI command, so a locked-out administrator with shell access is never stuck.
- Reports, audit logs, domain blocks, account suspension, silencing and disabling.
- User administration from the shell: create, list, show, approve, reject, suspend, unsuspend, silence, unsilence, disable, enable, delete, reset a password, set an email, disable MFA, refresh remote actors.
- Relay management, invitations, media administration, and a queue view that deliberately omits payload bodies.
- An SMTP test that only ever sends to the current administrator's own address.
- An effective-configuration page, so "what is this server actually running" is a question with an answer.
littlefedi --config littlefedi.toml admin create-user --username alice --admin
littlefedi --config littlefedi.toml admin list-users
littlefedi --config littlefedi.toml admin reset-password --username alice
littlefedi --config littlefedi.toml admin domain-block add example.invalid
The instance profile
What a server says about itself lives in one place, /admin/instance,
and changes apply without a restart.
| Field | Where it appears |
|---|---|
| Short description | Server directories, NodeInfo, the header of /about |
| Description | The top of /about, GET /api/v1/instance |
| Extended description | /about in full, GET /api/v1/instance/extended_description |
| Rules | /about, the registration form, GET /api/v1/instance/rules |
| Moderation contact | /about, the email field of both instance APIs |
| Terms of service | /terms, GET /api/v1/instance/terms_of_service |
The extended description and the terms are Markdown, rendered and sanitized
before publication. Leave the terms empty and this server publishes none:
/terms and its API endpoint answer 404, so clients hide the link
rather than opening an empty page.
The same fields exist in the [instance] section of the config file,
which init offers to fill in. The config file is the seed and the
admin console is the override; the console writes to the database rather than
back to the file. Pick one of the two and stay there.
Rules do not follow the per-field fallback. Saving the profile even once makes the stored rules authoritative, including an empty list, so an administrator who clears the box publishes no rules instead of silently getting the configured ones back.
Media
Local disk or an S3-compatible object store, decided by a build tag and a configuration key. Two size knobs do different jobs and are easy to confuse:
[media]
max_file_size = 41943040 # bytes accepted per upload (default 40 MB)
max_stored_dimension = 2560 # downscale stored JPEG/PNG to this longest side
max_image_pixels = 0 # reject images decoding to more than this (0 = no limit)
max_stored_dimension is what keeps the media directory small.
Uploads are downscaled once, on arrival, and every later read moves the smaller
file: federation fetches, cache warming, backups. It applies to cached remote
media too, and it is destructive, because no full-resolution copy is kept.
Animated GIFs, WebP, video and audio are never re-encoded.
max_image_pixels bounds decoded memory instead, which a file-size
limit cannot: a small compressed file can declare an enormous canvas and decode
into gigabytes. Downscaling does not replace that bound, because an image must
be decoded in full before it can be shrunk.
Files under /media/ use unguessable public URLs, the same way
Mastodon does, so images embedded in federated posts render without a
littleFedi login. Status pages, API metadata and federation objects still
enforce visibility, but anyone who obtains a raw media URL can fetch that
file.
API, clients and streaming
A Mastodon-compatible OAuth flow and REST API, WebSocket and SSE streaming, and Web Push. Existing Mastodon clients connect and work. The legacy flow without PKCE is still supported, because some clients still need it.
Discovery endpoints for trends, suggestions and endorsements return empty
results on purpose. Featured tags are implemented. Remote custom emoji are
parsed and rendered; local custom-emoji upload and management is not
implemented, so this server emits no local /emojis/{id} objects.
Your data: export, import, exit
An account can export itself, posts and media included, and import that archive into another littleFedi instance, along with its preferences, its custom CSS and its blog stylesheet. Moving on should not mean deleting your history. Account migration and account deletion are supported, and deletion emits signed activities that other servers actually receive, as long as you keep the queue workers running while decommissioning.
There is also an instance self-destruct command. A server that cannot be shut down cleanly leaves debris on every peer that ever heard of it, and that is somebody else's problem you created.
An officially supported one-off path exists for moving a single-user Akkoma or Pleroma instance onto littleFedi on the same domain, keeping the actor URI and the RSA key pair so that remote servers never notice the switch. It carries the identity, the profile, the followers and the following list. It does not carry old posts or media.
Storage and build variants
Optional dependencies are kept out of the default binary by build tags, so the server you run on a small machine does not carry a PostgreSQL driver it will never open.
| Command | Tag | Adds |
|---|---|---|
make build | none | Embedded SQLite, local media. The default. |
make build-postgres | postgres | PostgreSQL storage and cross-process streaming |
make build-s3 | s3 | S3-compatible media storage and migration |
make build-postgres-s3 | both | The clustered profile |
make build-blog | blog | Static blog generation |
Interface, languages, accessibility
The web interface is rendered on the server. JavaScript improves it and is never required by it: the composer, the timelines, the settings pages, the moderation tools and the admin console all work with scripting off.
- Ten themes, four of which dress the interface as a native application on macOS, Plasma, GNOME or Xfce, plus light, dark, true black and automatic modes, and a per-account custom stylesheet. See Theming.
- Six interface languages: English, German, Spanish, French, Italian and Portuguese. A translation is not merged half-finished, because a completeness test fails.
- Built-in themes are contrast-tested in the repository: text and muted text against every surface they can land on.
Operations
/healthreports process health,/readyzchecks readiness including storage, and/metricsexposes Prometheus metrics when enabled.- The process logs structured lines to stdout and lets the service manager decide where they go. Nothing in littleFedi assumes systemd, or syslog, or SMF.
- Built-in backups of the database, the configuration and, optionally, local owned media, with a manifest and a restore command.
- Housekeeping, remote media sweeps and periodic backups run on one designated process, so a cluster does not need a distributed election to decide who vacuums.
- Written runbooks for failed migrations, full disks, stuck queues, compromised keys, spam bursts, SMTP outages, media outages and database outages.
Known limits
- Trends, suggestions and endorsements are intentionally empty.
- Local custom emoji cannot be uploaded or managed.
- Blog generation is process-local. One process only.
- There is no general read-only mode. Closing registration and rejecting write paths at the edge is the documented substitute.
- The application rate limiter is per process, so a multi-process deployment needs a shared limit at the reverse proxy.
- The built-in media backup covers local owned media only. With
media.backend = "s3", asking forinclude_media = trueis rejected rather than producing an incomplete backup.