DEV Community

Cover image for Macula - An Ecosystem for Mesh-native Computing
R.G. Lefever
R.G. Lefever

Posted on

Macula - An Ecosystem for Mesh-native Computing

Macula is a mesh network for services and applications, built on Erlang/OTP: a node dials out over QUIC to one or more relay stations, which route its RPC calls, fan out its pub/sub events, and move content and streams between it and everyone else on the mesh. No inbound ports on the client side, no VPN, no central broker any one operator controls. Think of it less as "a client library for my server" and more as "the internet is one big distributed event bus, and here's how you dial into it."

It's been in continuous development since late 2025, macula (the core SDK) is at v10.14.1 on hex.pm, and the station side runs our own production fleet today. Four language ports exist beyond Erlang (Go, Rust, PHP, .NET), plus a CLI, an MCP server, and the relay itself.

What's actually in the SDK, and what it's built on

macula (hex.pm, Erlang/OTP) is the client side: the primitives an application dispatches against. macula-station (separate repo, also Erlang/OTP) is the relay: it's what actually does the routing, and it's where most of the algorithmic work lives.

  • QUIC (HTTP/3) transport: every connection is outbound-only, so a client never needs an open inbound port. Independent stream multiplexing means one slow stream doesn't head-of-line-block the others (the actual problem QUIC was designed to fix over TCP-based HTTP/2), and connection migration means a session survives a network change, which matters once you're talking to a phone switching from wifi to cellular mid-call. TLS 1.3 is built in, not bolted on. The transport itself is a Rust NIF (Quinn underneath), shared by the SDK and the station.
  • Kademlia DHT: the routing table. Signed, TTL'd records for advertisements, endpoints, and presence, resolved via multi-round iterative lookup. This answers "which station is this capability actually reachable through": resolve once against the DHT, then dial that station directly. One hop, no relay sitting in the data path afterward.
  • SWIM-Lifeguard: membership and failure detection, no central heartbeat server. Direct and indirect probing plus gossiped dissemination is plain SWIM; Lifeguard adds the adaptive suspicion timeout that keeps a busy node from getting falsely marked dead under load.
  • HyParView + Plumtree: the newest layer (landed in the core SDK around 9.2.0-10.5.0, additive, no supervised OTP wrapper around it yet). HyParView keeps a bounded partial view of the mesh per realm instead of every station knowing about every other station; Plumtree builds an epidemic broadcast tree over that view so a fact reaches everyone without flooding the whole mesh to do it. This is what keeps pub/sub fan-out and multi-hop RPC relay from becoming an all-to-all problem as the mesh grows.
  • Content addressing: BLAKE3-hashed blocks, MCID identifiers, chunked put/get with discovery. Cancel is a real peer-visible QUIC RESET_STREAM, not a local give-up that leaves the other side hanging.
  • Identity and authorization: Ed25519 keypairs, UCAN capability tokens (mint/verify/introspect), DID documents, NIF-accelerated. Authorization is capability-based, a UCAN the caller presents, not a session or account the station has to look up.

macula-station is realm-agnostic and holds no application data of its own, realm membership and identity belong to whatever connects through it. One process, one Ed25519 identity, peers outbound with other stations to form the mesh; run one and it's an island, give it peers and it's part of something bigger. It's been running our own multi-station fleet continuously since 2026-04-14 (500+ commits at this point) against real traffic: DHT, SWIM, pub/sub relay, RPC relay (including genuine multi-hop, a call can cross two stations with no direct peering edge between them), streaming relay, content transfer.

It's already got real consumers beyond its own tooling. hecate-services (a separate org) builds independently-deployed services on Macula: RAG, an LLM gateway, DNS, git, mail, each an ordinary outbound-only Macula client, several sharing a common substrate library, hecate-om, for identity, health, and capability-advertising boilerplate. The most fun one to actually look at is hecate-whiteboard: a real-time collaborative whiteboard, Miro minus the company, a host runs it on their own node and collaborators dial into that host over Macula to draw together. It talks to Macula directly rather than through hecate-om, and cross-node sync is still the next piece; today it's real-time within one host, but the mesh-hosting and identity side already works end to end.

Beyond Erlang: the other language ports

Four ports exist so far: Go, Rust, PHP, .NET, each a from-scratch reimplementation of the wire protocol, not a wrapper around the Erlang node. All are real, shipped, hex/npm-equivalent published.

The Rust one is worth a closer look: its own README tagline is "mobile first, not mobile-only". It's feature-complete for a leaf/edge client and live-verified against the production station fleet: handshake (pinned or WebPKI trust), unary RPC, pub/sub, content transfer, and streaming RPC in both caller and provider roles, direct-dial with cert-chain authorization, UCAN mint/verify/introspect, and a per-platform-overridable KeyStore for identity persistence. A separate macula-rust-ffi crate (structured the way iroh-ffi sits on top of iroh) wraps almost that entire surface with UniFFI and generates Kotlin and Swift bindings on every push, CI-checked both ways: one Rust core, generated bindings on both mobile platforms, instead of hand-maintaining two implementations of the same wire protocol. The mobile apps built on top of it are still early; the FFI layer underneath them is not.

The tools: what you actually run

Everything above is what you build with. Three things exist to actually run:

  • macula-cli: a small, scriptable Go binary, no TUI, no interactive mode. Every command works identically with or without --json; human-readable output is a formatting choice, not a separate code path. connect stages the handshake so a failure names which stage broke, call/serve do request/reply from either side, pubsub watch/publish stream events as newline-delimited JSON, and there's a -direct mode that resolves and dials a station straight from its DHT advertisement instead of trusting gossip. It can also run as a background daemon for persistent subscriptions/served procedures rather than exiting after one call.
  • macula-mcp: an MCP server, so any agent harness that speaks Model Context Protocol (Claude Code, Cursor, Cline, Continue) gets five tools, mesh_call, mesh_put, mesh_get, mesh_publish, mesh_watch, that invoke a peer's advertised capability, exchange content-addressed artifacts, and emit or watch facts on a topic, over the real wire protocol. It's deliberately thin: no QUIC/DHT logic of its own, it shells out to macula-cli for every mesh operation.
  • macula-station: covered above, the relay itself. Docker image on ghcr.io if you want to run one rather than just connect to one.

Where to find it

Everything's Apache-2.0. Happy to go deeper on any one piece of this, especially the overlay work since that's the newest and least battle-tested part of the stack.

Top comments (0)