<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[## LittleFedi: light, yet complete]]></title><description><![CDATA[<h2>LittleFedi: light, yet complete</h2>
<p dir="auto">I’m writing this post from a Raspberry Pi Zero W - 512 MB RAM, single-core ARMv6 - running NetBSD, powered by littleFedi. The process sits at 33 MB RSS, CPU basically asleep:</p>
<pre><code>load averages:  0.05,  0.08,  0.09
CPU states:  0.0% user,  0.0% nice,  0.0% system,  1.0% interrupt, 99.0% idle
Memory: 301M Free
  PID COMMAND             RES STATE
 2082 littlefedi-armv6    33M kqueue
</code></pre>
<p dir="auto">No Redis. No PostgreSQL (but optional). No Sidekiq. No Node.js build pipeline. One statically-linked binary, one SQLite file, and the full fediverse experience. And this is the part people tend to miss: the same binary that runs happily on a Pi Zero scales, on the right hardware, to numbers that have nothing to do with “lightweight”. It’s not a toy that stays a toy. It’s built to grow when you need it to.</p>
<h3>What LittleFedi actually ships</h3>
<p dir="auto"><strong>Federation</strong> - Full ActivityPub S2S: WebFinger, NodeInfo 2.1, host-meta, HTTP Signatures with anti-impersonation checks. Per-user and shared inboxes, outbox, followers, following, featured collections. Thread completion with bounded on-demand fetching of missing ancestors/replies (separate sync and background budgets, all hard-capped). Quote posts via FEP-044f with the full approval handshake (<code>QuoteRequest</code> -&gt; <code>QuoteAuthorization</code>), matching Mastodon 4.4 semantics, plus <code>_misskey_quote</code> and Fedibird <code>quoteUri</code> aliases. Account migration (<code>Move</code>), both outgoing and incoming, with <code>alsoKnownAs</code> linking, automatic follower migration, follow import from AP collections, and CSV export/import. Remote interaction discovery - async like/boost resolution from origin servers with REST fallback.</p>
<p dir="auto"><strong>Mastodon API</strong> - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with <code>replies_policy</code> and <code>exclusive</code>), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federated <code>Block</code>/<code>Undo</code>), mutes with duration/expiry and <code>hide_notifications</code>, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (<code>read</code>/<code>write</code>/<code>admin:read</code>/<code>admin:write</code>). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.</p>
<p dir="auto"><strong>Streaming</strong> - WebSocket and SSE. In-process pub/sub hub with per-connection send buffers, zero cost when no client is connected. Broadcast streams for public, local, remote, hashtags and lists. Per-account streams for user timeline, notifications and direct messages. Optional PostgreSQL <code>LISTEN</code>/<code>NOTIFY</code> backend for cross-process fan-out. Mastodon-compatible event serialization.</p>
<p dir="auto"><strong>Push notifications</strong> - Full Web Push / VAPID (RFC 8030/8291) with <code>aes128gcm</code> encryption. Per-type alert toggles (mention, follow, reblog, favourite, poll, follow_request, status). Notify-bell support on followed accounts. Subscription expiry detection, rate-limit handling, 5-retry delivery.</p>
<p dir="auto"><strong>Media pipeline</strong> - Upload processing: thumbnail generation (600x600), blurhash computation, EXIF stripping, magic-byte validation, SVG rejection, MIME mismatch detection, UUID-based file renaming. Size limits (40 MB default), pixel caps (16 MP default, tunable down to 4 MP for SBCs).</p>
<p dir="auto"><strong>Media privacy proxy</strong> - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (<code>/proxy/media?url=...&amp;sig=...</code>), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pure <code>io.Copy</code> pass-through, no disk, no decode, ~32 KB buffer. Forwards HTTP <code>Range</code> requests for audio/video seeking. Configure a <code>proxy_secret</code> for stable URLs across restarts. On low-RAM devices, set <code>cache_remote = "off"</code> and you still see every image on the fediverse, the instance just doesn’t store or process them.</p>
<p dir="auto"><strong>Remote media caching</strong> - Three modes: <code>off</code>, <code>eager</code> (background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch), <code>lazy</code> (cache on first access). Content-addressed, deduplicated by origin URL. Age-based pruning with file GC. Negative-cache for permanently dead URLs. Transparent origin fallback on cache miss. Open Graph preview cards stored durably with posts.</p>
<p dir="auto"><strong>S3-compatible storage</strong> - A separate build tag (<code>-tags s3</code>), deliberately excluded from the default binary to keep it small. Supports AWS S3, MinIO, SeaweedFS, Ceph, Backblaze B2, Wasabi, DigitalOcean Spaces. Native media migration CLI: <code>littlefedi admin media storage-migrate</code> between local and S3 (DB-queue-backed, resumable, bounded batches). <code>storage-status</code>, <code>storage-cancel</code>, <code>storage-resume</code> commands. <code>storage-manifest</code> for rclone JSONL integration.</p>
<p dir="auto"><strong>Markdown posts</strong> - Powered by goldmark with GFM extensions: tables, strikethrough, bare URL autolinking, hard wraps. Raw HTML deliberately not rendered. Output sanitized through bluemonday (defense-in-depth). Composer toggle in the web UI. Federates <code>source.mediaType: text/markdown</code> (Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.</p>
<p dir="auto"><strong>Visibility modes</strong> - The standard four (public, unlisted, private, direct) plus local-only (<code>local</code>, instance timeline only, never federates) and local unlisted (<code>local_unlisted</code>, followers only, no federation). Useful for notes to your own instance community.</p>
<p dir="auto"><strong>Bubble timeline</strong> - Curated set of instances whose posts appear alongside local posts in a special timeline. Akkoma-compatible extension. Admin panel for adding/removing bubble instances. API endpoint at <code>/api/v1/timelines/bubble</code>.</p>
<p dir="auto"><strong>Moderation</strong> - Account states: suspended (tombstone, federates <code>Delete(Person)</code>), silenced (visible to followers only, dropped from public timelines), quiet (like silenced plus it downgrades federation to followers-only, a middle ground I haven’t seen anywhere else), disabled (cannot log in, content stays visible). Self-suspend prevention, last-admin-demotion guard. Blocks (bidirectional, federated), mutes (local-only, with duration and <code>hide_notifications</code>), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwarded <code>Flag</code> into an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (<code>noop</code>/<code>silence</code>/<code>suspend</code>) plus Mastodon-parity options (<code>reject_media</code> enforced in the proxy, <code>reject_reports</code>, <code>obfuscate</code>, <code>public</code>). A moderation audit log records every admin action.</p>
<p dir="auto"><strong>Web UI</strong> - Server-rendered HTML with <code>html/template</code>, templates embedded via <code>//go:embed</code>. Inline CSS (dark mode, Inter font, gradients). htmx 2.x and Alpine.js for progressive enhancement. No build step, no Webpack, no Tailwind, no npm. Every action works as a plain form POST without JavaScript. Works in Lynx, eLinks, text-only browsers, and on mobile.</p>
<p dir="auto">Full feature set: home/public/local/bubble timelines with infinite scroll, profile pages with follow/unfollow/bell toggle, status threads with reply composer and background thread completion, post creation with CW, visibility selector (6 modes), media upload with alt text, Markdown toggle, quote posts (pre-loads composer with the quoted post as an inline card), post editing, composer autocomplete for <code>@mentions</code> and <code>#hashtags</code>, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.</p>
<p dir="auto"><strong>Admin panel</strong> - Dashboard (user counts, pending approvals, unreachable instances, open reports), accounts (with suspend/silence/quiet/disable/approve/reject actions), invites (CRUD), domain blocks (with severity and options), reports (triage and resolution), audit log, instance health (per-instance status with follower/following counts, reachability tracking, purge with typed-domain confirmation), bubble instances, settings, housekeeping (on-demand pruning), queue console (ready/scheduled/running/failed by job kind), media storage (S3 migration controls in S3 builds).</p>
<p dir="auto"><strong>Background jobs</strong> - DB-backed queue that survives restarts, 8 job kinds: <code>inbox</code>, <code>delivery</code> (16 attempts over roughly 26h with capped exponential backoff and equal jitter), <code>push_notification</code>, <code>actor_refresh</code>, <code>poll_close</code>, <code>scheduled_status</code>, <code>media_cache</code>, <code>media_migration</code>. Per-instance circuit breaker suspends delivery at <code>backoff_count &gt;= 10</code>. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.</p>
<p dir="auto"><strong>Backups</strong> - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with <code>config.toml</code>, a portable database dump (<code>VACUUM INTO</code> for SQLite, <code>pg_dump</code> for PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (<code>24h</code>, <code>7d</code>, whatever fits), decide whether to include owned media (the remote cache is always excluded, no point backing up other people’s content), and set a retention count so old backups get pruned automatically. Off by default, one line to turn on.</p>
<p dir="auto"><strong>Housekeeping</strong> - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (&gt;24h unattached), orphaned media (deleted posts), unreferenced media files (disk files with no DB record), cached media by age, cache file GC. On-demand controls in the admin UI.</p>
<p dir="auto"><strong>Security</strong> - Token-bucket rate limiting per IP. Security headers: <code>X-Content-Type-Options</code>, <code>X-Frame-Options</code>, <code>Referrer-Policy</code>, <code>Strict-Transport-Security</code>. Content-Security-Policy with nonce-based script/style. CORS. CSRF on all cookie-authenticated POSTs. Session fixation protection. Password reset token in a cookie, not the URL. OAuth consent screen (not auto-issuing). SSRF protection (DNS, IP and redirect re-validation) on all outbound HTTP. HTTP Signature algorithm enforcement (<code>rsa-sha256</code> only). Inbox body size limit (1 MB). Backfill goroutine cap. Thread-fetch amplification limits. File upload validation (magic bytes, SVG rejection, MIME mismatch). Username enumeration hardening.</p>
<p dir="auto"><strong>Operational</strong> - Prometheus metrics at <code>/metrics</code> (counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (<code>/health</code>, <code>/readyz</code>). CLI: <code>admin create-user</code>, <code>admin set-admin</code>, <code>admin list-users</code>, <code>admin suspend/unsuspend</code>, <code>admin invite generate/list/revoke</code>, <code>admin media prune/prune-orphans/prune-files</code>, <code>admin media storage-migrate/status/cancel/resume/manifest</code>, <code>post</code> (publish from stdin/file with Markdown, visibility, CW, media, reply, quote), <code>migrate</code> (run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (<code>LITTLEFEDI_{SECTION}_{KEY}</code>).</p>
<p dir="auto"><strong>Platform support</strong> - CGO-free, compiles with <code>CGO_ENABLED=0</code>. 23+ <code>GOOS/GOARCH</code> combos via the modernc SQLite driver: macOS (amd64, arm64), Linux (386, amd64, arm, arm64, loong64, ppc64le, riscv64, s390x), FreeBSD (386, amd64, arm, arm64), Windows (386, amd64, arm64), OpenBSD (amd64, arm64). NetBSD (amd64, arm, arm64) via a WASM-based fallback SQLite driver, I don’t think anything else in the fediverse space explicitly targets NetBSD. PostgreSQL is a separate build tag (<code>-tags postgres</code>), S3 is another (<code>-tags s3</code>). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.</p>
<h3>Why it matters</h3>
<p dir="auto">The fediverse shouldn’t demand a beefy VPS. It shouldn’t require Docker, 2 GB of RAM, Redis, Sidekiq, or a JS toolchain that pulls in 800 packages. A 10 euro Raspberry Pi Zero W running NetBSD, sitting on a shelf, drawing less than 2 watts, can be a fully functional fediverse instance with a web UI, mobile app compatibility, streaming, push notifications, quote posts, account migration, and a moderation toolkit. That’s not hypothetical, that’s what this post is running on.</p>
<p dir="auto">But don’t mistake “runs on a Pi Zero” for “only runs on a Pi Zero”. Point the same binary at real hardware and it scales to numbers that have nothing to do with hobby-instance territory. Low power is the floor, not the ceiling.</p>
<p dir="auto">One binary, one config file, one SQLite database. Light, yet complete.</p>
<p dir="auto">I’ve been involved in this project for a while now, though I can’t say much more about it at the moment, there are other people involved besides me and it’s not entirely my call to talk about it publicly yet.</p>
<p dir="auto">#Fediverse #littleFedi #OwnYourData #NetBSD</p>
]]></description><link>https://forum.fedi.dk/topic/33c5358b-65b1-418a-8771-7bbf833727de/littlefedi-light-yet-complete</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Jul 2026 11:25:34 GMT</lastBuildDate><atom:link href="https://forum.fedi.dk/topic/33c5358b-65b1-418a-8771-7bbf833727de.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 01 Jul 2026 15:58:03 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 18:08:02 GMT]]></title><description><![CDATA[<p><span><a href="https://rpi0w.stefanomarinelli.it/@stefano">@<span>stefano</span></a></span> </p><p>Are you using a USB Ethernet adapter on the Zero?</p><p>Something like this:</p><p><a href="https://plugable.com/products/usb2-otge100" rel="nofollow noopener"><span>https://</span><span>plugable.com/products/usb2-otg</span><span>e100</span></a></p>]]></description><link>https://forum.fedi.dk/post/https://mastodon.bsd.cafe/users/mjack/statuses/116846053481167667</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://mastodon.bsd.cafe/users/mjack/statuses/116846053481167667</guid><dc:creator><![CDATA[mjack@mastodon.bsd.cafe]]></dc:creator><pubDate>Wed, 01 Jul 2026 18:08:02 GMT</pubDate></item><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 17:38:53 GMT]]></title><description><![CDATA[<p><span><a href="https://rpi0w.stefanomarinelli.it/@stefano" rel="nofollow noreferrer noopener">@<span>stefano</span></a></span> The only issue your post has is, that you haven't said where to get LittleFedi.</p>]]></description><link>https://forum.fedi.dk/post/https://amikejo.xyz/users/nlupo/statuses/01KWFC692Y2YVE43A86DF8CQ28</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://amikejo.xyz/users/nlupo/statuses/01KWFC692Y2YVE43A86DF8CQ28</guid><dc:creator><![CDATA[nlupo@amikejo.xyz]]></dc:creator><pubDate>Wed, 01 Jul 2026 17:38:53 GMT</pubDate></item><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 17:07:25 GMT]]></title><description><![CDATA[<p><a href="/@rl_dane@polymaths.social" rel="nofollow noreferrer">@rl_dane@polymaths.social</a> <img src="https://forum.fedi.dk/assets/plugins/nodebb-plugin-emoji/emoji/android/1f609.png?v=7979fdcf9c7" class="not-responsive emoji emoji-android emoji--wink" style="height:23px;width:auto;vertical-align:middle" title=";-)" alt="😉" /></p>]]></description><link>https://forum.fedi.dk/post/https://rpi0w.stefanomarinelli.it/objects/da5c5005-47b7-42de-b0ac-85c69db69373</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://rpi0w.stefanomarinelli.it/objects/da5c5005-47b7-42de-b0ac-85c69db69373</guid><dc:creator><![CDATA[stefano@rpi0w.stefanomarinelli.it]]></dc:creator><pubDate>Wed, 01 Jul 2026 17:07:25 GMT</pubDate></item><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 17:03:30 GMT]]></title><description><![CDATA[<p><span><a href="https://rpi0w.stefanomarinelli.it/@stefano" rel="nofollow noreferrer noopener">@<span>stefano</span></a></span></p><p>Ok, you've got me insanely curious, now. <img src="https://forum.fedi.dk/assets/plugins/nodebb-plugin-emoji/emoji/android/1f601.png?v=7979fdcf9c7" class="not-responsive emoji emoji-android emoji--grin" style="height:23px;width:auto;vertical-align:middle" title="😁" alt="😁" /></p>]]></description><link>https://forum.fedi.dk/post/https://polymaths.social/users/rl_dane/statuses/01KWFA5GDDM0RGEY2RED7EGVV6</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://polymaths.social/users/rl_dane/statuses/01KWFA5GDDM0RGEY2RED7EGVV6</guid><dc:creator><![CDATA[rl_dane@polymaths.social]]></dc:creator><pubDate>Wed, 01 Jul 2026 17:03:30 GMT</pubDate></item><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 16:37:19 GMT]]></title><description><![CDATA[<p><a href="/@philip@social.wittamore.fr" rel="nofollow noreferrer">@philip@social.wittamore.fr</a> oh, that's strange. I'll check how they appear on snac</p>]]></description><link>https://forum.fedi.dk/post/https://rpi0w.stefanomarinelli.it/objects/4c01d27a-b0dd-4feb-83b7-488cd3d8d7ac</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://rpi0w.stefanomarinelli.it/objects/4c01d27a-b0dd-4feb-83b7-488cd3d8d7ac</guid><dc:creator><![CDATA[stefano@rpi0w.stefanomarinelli.it]]></dc:creator><pubDate>Wed, 01 Jul 2026 16:37:19 GMT</pubDate></item><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 16:22:58 GMT]]></title><description><![CDATA[Looking forward to trying it out!<br /><br />I noticed that the tag links in your post don't point to server/tags/tag but point to /tags/tag which results in a 403 if I don't have that tag on my own server.<br /><br />CC: <span><a href="https://rpi0w.stefanomarinelli.it/users/stefano">@stefano@rpi0w.stefanomarinelli.it</a></span><br />]]></description><link>https://forum.fedi.dk/post/https://social.wittamore.fr/philip/p/1782922978.788505</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://social.wittamore.fr/philip/p/1782922978.788505</guid><dc:creator><![CDATA[philip@social.wittamore.fr]]></dc:creator><pubDate>Wed, 01 Jul 2026 16:22:58 GMT</pubDate></item><item><title><![CDATA[Reply to ## LittleFedi: light, yet complete on Wed, 01 Jul 2026 16:02:03 GMT]]></title><description><![CDATA[<p><span><a href="https://rpi0w.stefanomarinelli.it/@stefano">@<span>stefano</span></a></span> beast machine <img src="https://forum.fedi.dk/assets/plugins/nodebb-plugin-emoji/emoji/android/1f60d.png?v=7979fdcf9c7" class="not-responsive emoji emoji-android emoji--heart_eyes" style="height:23px;width:auto;vertical-align:middle" title="😍" alt="😍" /></p>]]></description><link>https://forum.fedi.dk/post/https://mastodon.bsd.cafe/users/joany/statuses/116845558090260519</link><guid isPermaLink="true">https://forum.fedi.dk/post/https://mastodon.bsd.cafe/users/joany/statuses/116845558090260519</guid><dc:creator><![CDATA[joany@mastodon.bsd.cafe]]></dc:creator><pubDate>Wed, 01 Jul 2026 16:02:03 GMT</pubDate></item></channel></rss>