← All posts

Replacing the engine: MeshHold left libp2p behind

The biggest change in the project's life, and the one you're least likely to see: MeshHold no longer runs on libp2p. We spent this stretch pulling out the framework the whole thing was built on and dropping in our own networking — and if we got it right, the only thing you'll notice is your battery and data lasting longer.

A node's own networking stack, with the old framework ejected

This is the biggest change in the project's life, and the one you're least likely to notice: MeshHold no longer runs on libp2p. We spent this stretch pulling out the framework the whole thing was built on and dropping in networking of our own — and if we got it right, the only thing you'll see is your battery and your data lasting longer.

We removed libp2p

For most of its life, MeshHold was built on go-libp2p — a general-purpose peer-to-peer framework. It got us off the ground fast, and I don't regret starting there. But a general-purpose framework makes general-purpose choices, and an uncomfortable share of the problems I've been chasing for months traced straight back to living inside its defaults instead of ours: a phone bleeding gigabytes while it sat idle, a battery draining in a pocket, NAT traversal that leaned on relays. The hole punching we built, the single transport layer, the whole traffic diet — each was really a step out of libp2p's shadow.

This stretch we finished the job. The libp2p host is gone. A node's identity is our own key now. The encrypted handshake is ours too — rewritten so it's byte-for-byte what libp2p used to send, so nothing on the wire breaks and old and new nodes still talk. The gossip that carried chat and replication moved off libp2p's pubsub onto our own signed broadcast, and the dependency left go.mod entirely. The address book, the bootstrap, the transports — all ours, all things we can now tune for a private homelab cloud rather than for everybody's p2p app at once.

It was deep, invisible, slightly terrifying work: swapping the engine while trying not to let the car notice. Thousands of small, careful moves — a word after a dot is not a reference, a guard that was true because of the old transport keeps answering after you remove it — each one tested and committed on its own.

What it bought

The point of owning your foundation is that you finally get to make the choices your product actually needs. The headline one is a number I'm genuinely proud of: an idle node — just sitting there, part of your mesh, with nothing happening — now costs about a fifth of a megabyte an hour. That used to be measured in gigabytes a day. A phone can be a full, first-class node of your mesh and still last you the day.

And when there's genuinely nothing to say, the mesh now says nothing: a sleeping node and the relay that holds its place keep a quiet standby session instead of a constant back-and-forth, and a node that goes to sleep lets its radio actually power down. Quiet by default, loud only when you ask.

A fourth agent, and one socket for all of them

Alongside the networking surgery, the AI agents got a cleanup of their own. OpenAI's Codex joins Claude, Gemini and OpenCode — and all four now run on a single shared core, with one way to sign in, one session registry, and one account panel, instead of each being its own special case. Codex signs in three ways, remembers its work across a restart, can draw, and can answer from this node's own local model or borrow one from the mesh, exactly like the others.

That's the shape of this stretch: the engine replaced with one we built ourselves, and the cabin tidied while it was open. It's the kind of thing you do exactly once, right before a first release — because when people start depending on it, you want the foundation under them to be yours. 0.8 is close.


Since the last post

  • libp2p is gone: the libp2p host is deleted; a node's identity is our own key; the Noise handshake is ours, rewritten byte-for-byte compatible so the wire doesn't break; chat and replication moved off libp2p's pubsub onto our own signed (Ed25519) broadcast, and the dependency left go.mod
  • Our own networking: the peerstore, bootstrap, NAT punching and transport drivers are ours now, with an architecture guard that keeps anything above the transport from naming a driver
  • A quiet idle node: an idle node costs roughly 0.2 MB/hour, measured on a dedicated idle stand; sleeping nodes hold a standby session with their relay instead of constant chatter, and let the radio power down
  • Codex agent: OpenAI Codex joins Claude, Gemini and OpenCode — signs in three ways, draws, persists across restarts, and runs on this node's model or the mesh's
  • One agent core: all four agents moved onto a single shared backend core — one sign-in contract, one session registry, one account panel — and the per-agent adapters are gone
  • Tunnel/route split: the circuit and routing machinery moved into its own package, with the exit half cleanly separated from the client half, and the per-circuit key sealed to the exit's identity