littleMesh
Federating without a public address
An ordinary fediverse server needs a domain, a certificate, and port 443 reachable from the internet. Plenty of people have none of those, and their machine is perfectly good. littleMesh is for them.
The idea
littleMesh lets a littleFedi instance federate from behind home NAT, behind CGNAT, from a laptop, or from a Pi plugged into a router nobody administers.
It is an additional transport, not a new protocol. ActivityPub, WebFinger, HTTP Signatures, media fetches and the delivery queue all run over it unchanged. Nothing above the socket knows the difference between a circuit and a TCP connection.
The four roles
Only the node and one lighthouse are required.
| Role | Required? | Purpose |
|---|---|---|
| Node | Yes | The littleFedi instance behind NAT. Its Ed25519 key is its stable technical identity. |
| Lighthouse | Yes | Relays encrypted circuits. Stores no accounts, holds no deliveries, cannot impersonate a node. |
| HTTPS gateway | Only for ordinary fediverse access | Gives Mastodon, GoToSocial and other non-mesh servers public HTTPS access to mesh nodes. Normally runs beside a lighthouse. |
| Directory | Only for readable aliases or signed relay replacement | Maps a readable alias to a node ID and publishes the node's current pinned lighthouse locators. |
Each service can be provided by the littleFedi community or self-hosted. A directory does not replace a gateway, and a gateway does not replace a lighthouse.
Three ways to deploy it
A. Lighthouse only
Use this when every participating server runs littleFedi with littleMesh support. You get NAT traversal and encrypted, node-pinned circuits. Stock Mastodon and GoToSocial cannot reach the instance, and handles contain the full node ID. No gateway, no public certificate, no wildcard DNS, no directory.
[mesh]
enabled = true
suffix = "mesh.littlefedi.social"
lighthouses = [
"faro01.littlefedi.social:7842@<published-faro01-node-id>",
"faro02.littlefedi.social:7842@<published-faro02-node-id>",
]
exposure = "federation"
Copy the current lighthouse node IDs from the operator. Never invent or omit a production pin: the pin is what makes the relay untrusted rather than trusted.
B. Lighthouse plus HTTPS gateway
Use this when ordinary fediverse servers must reach the node and a long
self-certifying handle is acceptable. The gateway needs wildcard DNS and TLS.
The resulting handle looks like
@alice@<node-id>.mesh.littlefedi.social, which is honest but
not memorable.
C. Lighthouse, gateway and directory
The complete identity model: a readable handle, a technical actor ID that never changes, and a signed, replaceable route.
readable handle @alice@red-house.mesh.littlefedi.social
technical actor https://<node-id>.mesh.littlefedi.social/users/alice
current route signed list of pinned lighthouse addresses
The lighthouses may be community-hosted, self-hosted, or a mixture. The signed directory record tells gateways and mesh callers which relays currently carry the node. Replacing one of them changes neither the handle nor the actor ID.
Names, keys and readable handles
A mesh instance generates an Ed25519 key pair and takes its domain from it:
<node-id>.mesh.<lighthouse-host>. Because the name
is the public key, reaching that domain means reaching the holder of
that key and nobody else. There is no certificate authority in the path and
nothing to misissue.
That property is worth the ugly handle, but nobody wants to type it. The opt-in
v2 directory maps a readable alias onto the node ID: the visible handle becomes
readable while the actor ID stays
https://<node-id>.<stable-suffix>/.... The wizard in
littlefedi init asks for the alias domain, the directory URL, the
stable technical suffix and one to three pinned lighthouses, and prints the
exact alias-to-node-ID reservation the directory operator has to approve.
What you are trusting
This is the part to read before deciding, not after.
- The lighthouse cannot read your traffic
- The payload is a second TLS session pinned to both node IDs. The relay copies bytes. It cannot read them, alter them, or impersonate either side. It can refuse to carry traffic, and it can observe which node IDs talk to each other and when. littleMesh addresses reachability, not anonymity.
- The HTTPS gateway can
- It terminates TLS for callers who are not mesh members, so its operator can
read that traffic, tamper with GET responses, and drop requests. Signed POSTs
cannot be altered undetected. Mesh peers never traverse the gateway. If you
trust no relay at all, set
mesh.suffix = "mesh"and give up interoperability with the ordinary fediverse. - The directory is not a trust anchor
- It can assign and resolve an alias and serve signed lighthouse locators, but every record is verified against the node ID and every payload connection still pins that node ID. A compromised directory can censor discovery, or deceive the very first lookup of a human alias. It cannot impersonate a node ID a caller already knows.
- The mesh private key is an identity
- The key in the
mesh_identitytable is this instance on the mesh. Protect it and back it up exactly like the ActivityPub actor keys.
A mesh instance federates with other mesh-aware instances out of the box. Mastodon and friends cannot resolve a mesh name at all, and can only reach it if the lighthouse operator runs the optional HTTPS gateway. Nobody is obliged to.
Exposure defaults
Both the gateway and each node default to federation-only exposure. ActivityPub,
discovery and public /media/ URLs work; the web interface, login,
API and media proxy stay private unless full exposure is explicitly enabled.
| Mode | What it admits |
|---|---|
federation | The default. Federation, discovery and public media only. |
full | The complete instance, web interface and API included. |
allowlist | Federation for everyone, full access only for the listed identifiers. |
The two policies are independent and both apply: the gateway decides what ordinary internet clients may send through it, and the node decides again what it accepts from mesh peers.
Getting started
./littlefedi init # choose "instance", then answer yes to "Join littleMesh"
./littlefedi --config littlefedi.toml
./littlefedi --config littlefedi.toml mesh id
init can create an instance with either v2 readable aliases or the
long-ID fallback, and it can also write a lighthouse configuration if you are
the one running the relay. mesh id prints the node identity,
mesh peers lists what it is connected to, mesh ping
checks a peer, and mesh publish pushes a new signed locator record
to the directory.
Replacing a lighthouse
Relays fail, and relay operators stop volunteering. When one goes away, register
with the replacement, put both the old and the new pinned locators in
[mesh].lighthouses, and publish:
littlefedi --config /etc/littlefedi/littlefedi.toml mesh publish
Do not change mesh.alias, mesh.suffix,
local_domain, web_domain, or the mesh identity.
Confirm the published sequence increased, then remove the dead locator only
after caches have had one record TTL to refresh. The handle and the actor ID
must be byte-for-byte identical before and after: that is the whole point of
separating the name from the route.
Nodes can register with several pinned lighthouses at once. Failover works when the caller and the target share a backup relay. Lighthouses store no deliveries, so a node that is offline is not a lost post: the normal federation queue retries until it comes back.