How it works

A thin, opinionated layer over boring, proven pieces — and the reasoning behind each choice.

The pieces

PieceHolds
The app (Node, Express, tRPC) Identity, community structure, permissions. The only thing users talk to.
PostgreSQL Accounts, servers, channels, message index, file metadata, roles, bans.
Conduit (Matrix homeserver) Message transport. Every message is a real Matrix event.
Kubo (IPFS) The actual bytes of every shared file.
cloudflared (optional) Outbound-only tunnel, when you want a public address.

Either as Docker containers on one network, or as systemd services on one machine. Same architecture, different packaging.

A server is a Matrix Space

Creating a community creates a Space on your homeserver. Every channel inside it is a Matrix room, linked as a child of that Space — starting with #general, created for you.

This isn't decoration. It means a third-party Matrix client like Element can connect to your homeserver and see the same rooms, the same messages, the same power levels. Your community isn't trapped in this particular app.

The browser never talks to Matrix

This is the load-bearing decision, and it's worth explaining because most Matrix clients do the opposite.

The app provisions one Matrix account per user on first use and holds that access token server-side, in the database. It never reaches the browser. Every Matrix operation — sending, editing, reacting, joining, redacting — goes through the app, acting on the user's behalf over the internal network.

Three things fall out of that:

The cost is honest: the app is now on the critical path for every message, and it can read them. See security for what that means.

Files

Uploads go to your own IPFS node and are pinned there. The file's content identifier and metadata are recorded in PostgreSQL against the channel.

Downloads stream back through the app, not from a public IPFS gateway — so membership is checked on every request and content identifiers never leak to people outside the channel. Uploads cap at 50 MB.

Authority, in two layers

SOVRGNnet keeps its own roles — owner, admin, moderator, member — in PostgreSQL, ranked, and checks them on every operation. That's the authoritative layer.

Matrix power levels are kept in sync as a best-effort mirror, so third-party clients show a sensible picture. But the app never trusts the homeserver's opinion about who's allowed to do what. If the two ever disagree, the app's answer is the one that counts.

Nothing listens on your router

When you want a public address, it runs over an outbound tunnel: your machine connects out, and traffic comes back down that connection. No forwarded ports, no exposed home IP, nothing for a scanner to find.

The landing site you're reading is deliberately separate — static files on Cloudflare Pages, so this page stays up even when the homelab doesn't.

What's deliberately not here

What's honestly missing

The engineering-detail version lives in ARCHITECTURE.md, and the ordering of what's next is in ROADMAP.md.