## LittleFedi: light, yet complete
-
LittleFedi: light, yet complete
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:
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 kqueueNo 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.
What LittleFedi actually ships
Federation - 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 (
QuoteRequest->QuoteAuthorization), matching Mastodon 4.4 semantics, plus_misskey_quoteand FedibirdquoteUrialiases. Account migration (Move), both outgoing and incoming, withalsoKnownAslinking, 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.Mastodon API - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with
replies_policyandexclusive), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federatedBlock/Undo), mutes with duration/expiry andhide_notifications, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (read/write/admin:read/admin:write). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.Streaming - 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
LISTEN/NOTIFYbackend for cross-process fan-out. Mastodon-compatible event serialization.Push notifications - Full Web Push / VAPID (RFC 8030/8291) with
aes128gcmencryption. 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.Media pipeline - 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).
Media privacy proxy - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (
/proxy/media?url=...&sig=...), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pureio.Copypass-through, no disk, no decode, ~32 KB buffer. Forwards HTTPRangerequests for audio/video seeking. Configure aproxy_secretfor stable URLs across restarts. On low-RAM devices, setcache_remote = "off"and you still see every image on the fediverse, the instance just doesn’t store or process them.Remote media caching - Three modes:
off,eager(background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch),lazy(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.S3-compatible storage - A separate build tag (
-tags s3), 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:littlefedi admin media storage-migratebetween local and S3 (DB-queue-backed, resumable, bounded batches).storage-status,storage-cancel,storage-resumecommands.storage-manifestfor rclone JSONL integration.Markdown posts - 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
source.mediaType: text/markdown(Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.Visibility modes - The standard four (public, unlisted, private, direct) plus local-only (
local, instance timeline only, never federates) and local unlisted (local_unlisted, followers only, no federation). Useful for notes to your own instance community.Bubble timeline - 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
/api/v1/timelines/bubble.Moderation - Account states: suspended (tombstone, federates
Delete(Person)), 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 andhide_notifications), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwardedFlaginto an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (noop/silence/suspend) plus Mastodon-parity options (reject_mediaenforced in the proxy,reject_reports,obfuscate,public). A moderation audit log records every admin action.Web UI - Server-rendered HTML with
html/template, templates embedded via//go:embed. 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.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
@mentionsand#hashtags, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.Admin panel - 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).
Background jobs - DB-backed queue that survives restarts, 8 job kinds:
inbox,delivery(16 attempts over roughly 26h with capped exponential backoff and equal jitter),push_notification,actor_refresh,poll_close,scheduled_status,media_cache,media_migration. Per-instance circuit breaker suspends delivery atbackoff_count >= 10. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.Backups - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with
config.toml, a portable database dump (VACUUM INTOfor SQLite,pg_dumpfor PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (24h,7d, 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.Housekeeping - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (>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.
Security - Token-bucket rate limiting per IP. Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Strict-Transport-Security. 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 (rsa-sha256only). 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.Operational - Prometheus metrics at
/metrics(counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (/health,/readyz). CLI:admin create-user,admin set-admin,admin list-users,admin suspend/unsuspend,admin invite generate/list/revoke,admin media prune/prune-orphans/prune-files,admin media storage-migrate/status/cancel/resume/manifest,post(publish from stdin/file with Markdown, visibility, CW, media, reply, quote),migrate(run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (LITTLEFEDI_{SECTION}_{KEY}).Platform support - CGO-free, compiles with
CGO_ENABLED=0. 23+GOOS/GOARCHcombos 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 (-tags postgres), S3 is another (-tags s3). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.Why it matters
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.
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.
One binary, one config file, one SQLite database. Light, yet complete.
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.
#Fediverse #littleFedi #OwnYourData #NetBSD
-
LittleFedi: light, yet complete
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:
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 kqueueNo 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.
What LittleFedi actually ships
Federation - 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 (
QuoteRequest->QuoteAuthorization), matching Mastodon 4.4 semantics, plus_misskey_quoteand FedibirdquoteUrialiases. Account migration (Move), both outgoing and incoming, withalsoKnownAslinking, 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.Mastodon API - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with
replies_policyandexclusive), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federatedBlock/Undo), mutes with duration/expiry andhide_notifications, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (read/write/admin:read/admin:write). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.Streaming - 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
LISTEN/NOTIFYbackend for cross-process fan-out. Mastodon-compatible event serialization.Push notifications - Full Web Push / VAPID (RFC 8030/8291) with
aes128gcmencryption. 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.Media pipeline - 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).
Media privacy proxy - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (
/proxy/media?url=...&sig=...), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pureio.Copypass-through, no disk, no decode, ~32 KB buffer. Forwards HTTPRangerequests for audio/video seeking. Configure aproxy_secretfor stable URLs across restarts. On low-RAM devices, setcache_remote = "off"and you still see every image on the fediverse, the instance just doesn’t store or process them.Remote media caching - Three modes:
off,eager(background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch),lazy(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.S3-compatible storage - A separate build tag (
-tags s3), 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:littlefedi admin media storage-migratebetween local and S3 (DB-queue-backed, resumable, bounded batches).storage-status,storage-cancel,storage-resumecommands.storage-manifestfor rclone JSONL integration.Markdown posts - 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
source.mediaType: text/markdown(Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.Visibility modes - The standard four (public, unlisted, private, direct) plus local-only (
local, instance timeline only, never federates) and local unlisted (local_unlisted, followers only, no federation). Useful for notes to your own instance community.Bubble timeline - 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
/api/v1/timelines/bubble.Moderation - Account states: suspended (tombstone, federates
Delete(Person)), 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 andhide_notifications), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwardedFlaginto an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (noop/silence/suspend) plus Mastodon-parity options (reject_mediaenforced in the proxy,reject_reports,obfuscate,public). A moderation audit log records every admin action.Web UI - Server-rendered HTML with
html/template, templates embedded via//go:embed. 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.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
@mentionsand#hashtags, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.Admin panel - 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).
Background jobs - DB-backed queue that survives restarts, 8 job kinds:
inbox,delivery(16 attempts over roughly 26h with capped exponential backoff and equal jitter),push_notification,actor_refresh,poll_close,scheduled_status,media_cache,media_migration. Per-instance circuit breaker suspends delivery atbackoff_count >= 10. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.Backups - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with
config.toml, a portable database dump (VACUUM INTOfor SQLite,pg_dumpfor PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (24h,7d, 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.Housekeeping - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (>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.
Security - Token-bucket rate limiting per IP. Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Strict-Transport-Security. 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 (rsa-sha256only). 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.Operational - Prometheus metrics at
/metrics(counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (/health,/readyz). CLI:admin create-user,admin set-admin,admin list-users,admin suspend/unsuspend,admin invite generate/list/revoke,admin media prune/prune-orphans/prune-files,admin media storage-migrate/status/cancel/resume/manifest,post(publish from stdin/file with Markdown, visibility, CW, media, reply, quote),migrate(run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (LITTLEFEDI_{SECTION}_{KEY}).Platform support - CGO-free, compiles with
CGO_ENABLED=0. 23+GOOS/GOARCHcombos 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 (-tags postgres), S3 is another (-tags s3). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.Why it matters
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.
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.
One binary, one config file, one SQLite database. Light, yet complete.
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.
#Fediverse #littleFedi #OwnYourData #NetBSD
@stefano beast machine

-
LittleFedi: light, yet complete
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:
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 kqueueNo 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.
What LittleFedi actually ships
Federation - 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 (
QuoteRequest->QuoteAuthorization), matching Mastodon 4.4 semantics, plus_misskey_quoteand FedibirdquoteUrialiases. Account migration (Move), both outgoing and incoming, withalsoKnownAslinking, 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.Mastodon API - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with
replies_policyandexclusive), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federatedBlock/Undo), mutes with duration/expiry andhide_notifications, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (read/write/admin:read/admin:write). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.Streaming - 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
LISTEN/NOTIFYbackend for cross-process fan-out. Mastodon-compatible event serialization.Push notifications - Full Web Push / VAPID (RFC 8030/8291) with
aes128gcmencryption. 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.Media pipeline - 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).
Media privacy proxy - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (
/proxy/media?url=...&sig=...), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pureio.Copypass-through, no disk, no decode, ~32 KB buffer. Forwards HTTPRangerequests for audio/video seeking. Configure aproxy_secretfor stable URLs across restarts. On low-RAM devices, setcache_remote = "off"and you still see every image on the fediverse, the instance just doesn’t store or process them.Remote media caching - Three modes:
off,eager(background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch),lazy(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.S3-compatible storage - A separate build tag (
-tags s3), 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:littlefedi admin media storage-migratebetween local and S3 (DB-queue-backed, resumable, bounded batches).storage-status,storage-cancel,storage-resumecommands.storage-manifestfor rclone JSONL integration.Markdown posts - 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
source.mediaType: text/markdown(Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.Visibility modes - The standard four (public, unlisted, private, direct) plus local-only (
local, instance timeline only, never federates) and local unlisted (local_unlisted, followers only, no federation). Useful for notes to your own instance community.Bubble timeline - 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
/api/v1/timelines/bubble.Moderation - Account states: suspended (tombstone, federates
Delete(Person)), 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 andhide_notifications), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwardedFlaginto an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (noop/silence/suspend) plus Mastodon-parity options (reject_mediaenforced in the proxy,reject_reports,obfuscate,public). A moderation audit log records every admin action.Web UI - Server-rendered HTML with
html/template, templates embedded via//go:embed. 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.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
@mentionsand#hashtags, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.Admin panel - 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).
Background jobs - DB-backed queue that survives restarts, 8 job kinds:
inbox,delivery(16 attempts over roughly 26h with capped exponential backoff and equal jitter),push_notification,actor_refresh,poll_close,scheduled_status,media_cache,media_migration. Per-instance circuit breaker suspends delivery atbackoff_count >= 10. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.Backups - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with
config.toml, a portable database dump (VACUUM INTOfor SQLite,pg_dumpfor PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (24h,7d, 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.Housekeeping - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (>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.
Security - Token-bucket rate limiting per IP. Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Strict-Transport-Security. 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 (rsa-sha256only). 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.Operational - Prometheus metrics at
/metrics(counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (/health,/readyz). CLI:admin create-user,admin set-admin,admin list-users,admin suspend/unsuspend,admin invite generate/list/revoke,admin media prune/prune-orphans/prune-files,admin media storage-migrate/status/cancel/resume/manifest,post(publish from stdin/file with Markdown, visibility, CW, media, reply, quote),migrate(run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (LITTLEFEDI_{SECTION}_{KEY}).Platform support - CGO-free, compiles with
CGO_ENABLED=0. 23+GOOS/GOARCHcombos 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 (-tags postgres), S3 is another (-tags s3). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.Why it matters
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.
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.
One binary, one config file, one SQLite database. Light, yet complete.
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.
#Fediverse #littleFedi #OwnYourData #NetBSD
Looking forward to trying it out!
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.
CC: @stefano@rpi0w.stefanomarinelli.it
-
Looking forward to trying it out!
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.
CC: @stefano@rpi0w.stefanomarinelli.it@philip@social.wittamore.fr oh, that's strange. I'll check how they appear on snac
-
LittleFedi: light, yet complete
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:
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 kqueueNo 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.
What LittleFedi actually ships
Federation - 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 (
QuoteRequest->QuoteAuthorization), matching Mastodon 4.4 semantics, plus_misskey_quoteand FedibirdquoteUrialiases. Account migration (Move), both outgoing and incoming, withalsoKnownAslinking, 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.Mastodon API - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with
replies_policyandexclusive), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federatedBlock/Undo), mutes with duration/expiry andhide_notifications, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (read/write/admin:read/admin:write). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.Streaming - 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
LISTEN/NOTIFYbackend for cross-process fan-out. Mastodon-compatible event serialization.Push notifications - Full Web Push / VAPID (RFC 8030/8291) with
aes128gcmencryption. 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.Media pipeline - 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).
Media privacy proxy - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (
/proxy/media?url=...&sig=...), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pureio.Copypass-through, no disk, no decode, ~32 KB buffer. Forwards HTTPRangerequests for audio/video seeking. Configure aproxy_secretfor stable URLs across restarts. On low-RAM devices, setcache_remote = "off"and you still see every image on the fediverse, the instance just doesn’t store or process them.Remote media caching - Three modes:
off,eager(background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch),lazy(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.S3-compatible storage - A separate build tag (
-tags s3), 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:littlefedi admin media storage-migratebetween local and S3 (DB-queue-backed, resumable, bounded batches).storage-status,storage-cancel,storage-resumecommands.storage-manifestfor rclone JSONL integration.Markdown posts - 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
source.mediaType: text/markdown(Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.Visibility modes - The standard four (public, unlisted, private, direct) plus local-only (
local, instance timeline only, never federates) and local unlisted (local_unlisted, followers only, no federation). Useful for notes to your own instance community.Bubble timeline - 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
/api/v1/timelines/bubble.Moderation - Account states: suspended (tombstone, federates
Delete(Person)), 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 andhide_notifications), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwardedFlaginto an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (noop/silence/suspend) plus Mastodon-parity options (reject_mediaenforced in the proxy,reject_reports,obfuscate,public). A moderation audit log records every admin action.Web UI - Server-rendered HTML with
html/template, templates embedded via//go:embed. 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.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
@mentionsand#hashtags, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.Admin panel - 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).
Background jobs - DB-backed queue that survives restarts, 8 job kinds:
inbox,delivery(16 attempts over roughly 26h with capped exponential backoff and equal jitter),push_notification,actor_refresh,poll_close,scheduled_status,media_cache,media_migration. Per-instance circuit breaker suspends delivery atbackoff_count >= 10. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.Backups - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with
config.toml, a portable database dump (VACUUM INTOfor SQLite,pg_dumpfor PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (24h,7d, 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.Housekeeping - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (>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.
Security - Token-bucket rate limiting per IP. Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Strict-Transport-Security. 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 (rsa-sha256only). 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.Operational - Prometheus metrics at
/metrics(counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (/health,/readyz). CLI:admin create-user,admin set-admin,admin list-users,admin suspend/unsuspend,admin invite generate/list/revoke,admin media prune/prune-orphans/prune-files,admin media storage-migrate/status/cancel/resume/manifest,post(publish from stdin/file with Markdown, visibility, CW, media, reply, quote),migrate(run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (LITTLEFEDI_{SECTION}_{KEY}).Platform support - CGO-free, compiles with
CGO_ENABLED=0. 23+GOOS/GOARCHcombos 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 (-tags postgres), S3 is another (-tags s3). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.Why it matters
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.
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.
One binary, one config file, one SQLite database. Light, yet complete.
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.
#Fediverse #littleFedi #OwnYourData #NetBSD
Ok, you've got me insanely curious, now.

-
Ok, you've got me insanely curious, now.

-
LittleFedi: light, yet complete
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:
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 kqueueNo 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.
What LittleFedi actually ships
Federation - 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 (
QuoteRequest->QuoteAuthorization), matching Mastodon 4.4 semantics, plus_misskey_quoteand FedibirdquoteUrialiases. Account migration (Move), both outgoing and incoming, withalsoKnownAslinking, 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.Mastodon API - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with
replies_policyandexclusive), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federatedBlock/Undo), mutes with duration/expiry andhide_notifications, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (read/write/admin:read/admin:write). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.Streaming - 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
LISTEN/NOTIFYbackend for cross-process fan-out. Mastodon-compatible event serialization.Push notifications - Full Web Push / VAPID (RFC 8030/8291) with
aes128gcmencryption. 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.Media pipeline - 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).
Media privacy proxy - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (
/proxy/media?url=...&sig=...), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pureio.Copypass-through, no disk, no decode, ~32 KB buffer. Forwards HTTPRangerequests for audio/video seeking. Configure aproxy_secretfor stable URLs across restarts. On low-RAM devices, setcache_remote = "off"and you still see every image on the fediverse, the instance just doesn’t store or process them.Remote media caching - Three modes:
off,eager(background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch),lazy(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.S3-compatible storage - A separate build tag (
-tags s3), 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:littlefedi admin media storage-migratebetween local and S3 (DB-queue-backed, resumable, bounded batches).storage-status,storage-cancel,storage-resumecommands.storage-manifestfor rclone JSONL integration.Markdown posts - 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
source.mediaType: text/markdown(Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.Visibility modes - The standard four (public, unlisted, private, direct) plus local-only (
local, instance timeline only, never federates) and local unlisted (local_unlisted, followers only, no federation). Useful for notes to your own instance community.Bubble timeline - 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
/api/v1/timelines/bubble.Moderation - Account states: suspended (tombstone, federates
Delete(Person)), 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 andhide_notifications), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwardedFlaginto an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (noop/silence/suspend) plus Mastodon-parity options (reject_mediaenforced in the proxy,reject_reports,obfuscate,public). A moderation audit log records every admin action.Web UI - Server-rendered HTML with
html/template, templates embedded via//go:embed. 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.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
@mentionsand#hashtags, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.Admin panel - 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).
Background jobs - DB-backed queue that survives restarts, 8 job kinds:
inbox,delivery(16 attempts over roughly 26h with capped exponential backoff and equal jitter),push_notification,actor_refresh,poll_close,scheduled_status,media_cache,media_migration. Per-instance circuit breaker suspends delivery atbackoff_count >= 10. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.Backups - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with
config.toml, a portable database dump (VACUUM INTOfor SQLite,pg_dumpfor PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (24h,7d, 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.Housekeeping - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (>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.
Security - Token-bucket rate limiting per IP. Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Strict-Transport-Security. 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 (rsa-sha256only). 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.Operational - Prometheus metrics at
/metrics(counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (/health,/readyz). CLI:admin create-user,admin set-admin,admin list-users,admin suspend/unsuspend,admin invite generate/list/revoke,admin media prune/prune-orphans/prune-files,admin media storage-migrate/status/cancel/resume/manifest,post(publish from stdin/file with Markdown, visibility, CW, media, reply, quote),migrate(run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (LITTLEFEDI_{SECTION}_{KEY}).Platform support - CGO-free, compiles with
CGO_ENABLED=0. 23+GOOS/GOARCHcombos 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 (-tags postgres), S3 is another (-tags s3). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.Why it matters
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.
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.
One binary, one config file, one SQLite database. Light, yet complete.
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.
#Fediverse #littleFedi #OwnYourData #NetBSD
@stefano The only issue your post has is, that you haven't said where to get LittleFedi.
-
LittleFedi: light, yet complete
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:
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 kqueueNo 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.
What LittleFedi actually ships
Federation - 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 (
QuoteRequest->QuoteAuthorization), matching Mastodon 4.4 semantics, plus_misskey_quoteand FedibirdquoteUrialiases. Account migration (Move), both outgoing and incoming, withalsoKnownAslinking, 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.Mastodon API - Broad coverage: timelines (home, public, local, hashtag, bubble, direct), status CRUD with edits, scheduled posts, polls, bookmarks, lists (with
replies_policyandexclusive), filters v2 (keyword CRUD), featured tags, followed hashtags (posts appear in home), markers, conversations, notifications with type exclusion, follow requests, blocks (federatedBlock/Undo), mutes with duration/expiry andhide_notifications, per-user domain blocks. OAuth2 with app registration, authorization code, client credentials and refresh_token flows, PKCE, consent screen, and scope enforcement (read/write/admin:read/admin:write). It talks fine to Elk, Tusky, Ivory, Phanpy, Semaphore and MastoBlaster.Streaming - 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
LISTEN/NOTIFYbackend for cross-process fan-out. Mastodon-compatible event serialization.Push notifications - Full Web Push / VAPID (RFC 8030/8291) with
aes128gcmencryption. 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.Media pipeline - 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).
Media privacy proxy - This is the part I actually care about most. All remote media streams through the instance via HMAC-signed URLs (
/proxy/media?url=...&sig=...), so local users never expose their IP address to remote servers. SSRF-guarded: DNS resolution check, private/CGNAT IP rejection, redirect re-validation. Pureio.Copypass-through, no disk, no decode, ~32 KB buffer. Forwards HTTPRangerequests for audio/video seeking. Configure aproxy_secretfor stable URLs across restarts. On low-RAM devices, setcache_remote = "off"and you still see every image on the fediverse, the instance just doesn’t store or process them.Remote media caching - Three modes:
off,eager(background sweep caches all remote attachments, avatars, headers and emoji, backfills existing on mode switch),lazy(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.S3-compatible storage - A separate build tag (
-tags s3), 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:littlefedi admin media storage-migratebetween local and S3 (DB-queue-backed, resumable, bounded batches).storage-status,storage-cancel,storage-resumecommands.storage-manifestfor rclone JSONL integration.Markdown posts - 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
source.mediaType: text/markdown(Pleroma/GTS convention). Inbound Markdown source is rendered to HTML.Visibility modes - The standard four (public, unlisted, private, direct) plus local-only (
local, instance timeline only, never federates) and local unlisted (local_unlisted, followers only, no federation). Useful for notes to your own instance community.Bubble timeline - 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
/api/v1/timelines/bubble.Moderation - Account states: suspended (tombstone, federates
Delete(Person)), 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 andhide_notifications), per-user domain blocks (distinct from admin instance-wide blocks). Reports pipeline: user submissions plus inbound/forwardedFlaginto an admin triage UI with resolution actions. Admin notification on new reports. Domain blocks with severity (noop/silence/suspend) plus Mastodon-parity options (reject_mediaenforced in the proxy,reject_reports,obfuscate,public). A moderation audit log records every admin action.Web UI - Server-rendered HTML with
html/template, templates embedded via//go:embed. 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.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
@mentionsand#hashtags, settings (display name, bio, password, sessions, moderation, pruning, account move, timeline preferences), report form, search page, tag management.Admin panel - 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).
Background jobs - DB-backed queue that survives restarts, 8 job kinds:
inbox,delivery(16 attempts over roughly 26h with capped exponential backoff and equal jitter),push_notification,actor_refresh,poll_close,scheduled_status,media_cache,media_migration. Per-instance circuit breaker suspends delivery atbackoff_count >= 10. Actor refresh dispatcher with stale-while-revalidate, crash-safe leases, and per-actor exponential backoff.Backups - Periodic or on-demand, server-side, no external tooling required. Each run produces a timestamped directory with
config.toml, a portable database dump (VACUUM INTOfor SQLite,pg_dumpfor PostgreSQL), an optional copy of owned media, and a manifest. Toggle it on, set an interval (24h,7d, 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.Housekeeping - Automated pruning: remote statuses by age, own low-interaction statuses (per-user or server thresholds, min likes/boosts caps), tombstones, expired mutes, stale media (>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.
Security - Token-bucket rate limiting per IP. Security headers:
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Strict-Transport-Security. 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 (rsa-sha256only). 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.Operational - Prometheus metrics at
/metrics(counters for API/inbox/fed/web requests, statuses, deliveries, thread fetches, queue depth; gauges for workers, pending follows, uptime). Health checks (/health,/readyz). CLI:admin create-user,admin set-admin,admin list-users,admin suspend/unsuspend,admin invite generate/list/revoke,admin media prune/prune-orphans/prune-files,admin media storage-migrate/status/cancel/resume/manifest,post(publish from stdin/file with Markdown, visibility, CW, media, reply, quote),migrate(run migrations only). SMTP for password reset and notifications (falls back to stdout). Config via TOML file plus environment variables (LITTLEFEDI_{SECTION}_{KEY}).Platform support - CGO-free, compiles with
CGO_ENABLED=0. 23+GOOS/GOARCHcombos 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 (-tags postgres), S3 is another (-tags s3). The default binary carries neither, keeping it small. ARMv6 (GOARM=6) gets special treatment in the release naming, that’s the Pi Zero target.Why it matters
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.
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.
One binary, one config file, one SQLite database. Light, yet complete.
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.
#Fediverse #littleFedi #OwnYourData #NetBSD
Are you using a USB Ethernet adapter on the Zero?
Something like this:
