sleipnir

sleipnir

Mesh - Capability-based routing for Processes on the BEAM

Hello everyone,

We’d like to share Mesh, a new open-source Elixir library developed by the Eigr community.

Mesh was created as part of the work on Spawn, where we needed a reliable and scalable way to route messages to specific groups of processes across a cluster, with deterministic behavior and minimal coordination overhead.

During Spawn’s development, we evaluated existing solutions such as :pg, Horde, and ProcessHub. These are solid libraries and work very well for many use cases. However, for our specific requirements — especially around capability-based routing, deterministic ownership, and very large-scale clustering — none of them fit exactly what we needed. Rather than forcing a model that didn’t align with our constraints, we decided to design a small, focused abstraction that could serve as a foundation for Spawn and similar systems.

That abstraction became Mesh.

What Mesh is

Mesh is a library for managing virtual processes and routing messages to them based on capabilities, rather than direct PIDs or global names.

At a high level, Mesh provides:

  • Capability-based process registration

  • Deterministic routing using shards

  • Decoupling between callers and process location

  • A model where any node can invoke a virtual process, and the system resolves where it should run

Processes register themselves with one or more capabilities, and callers route messages by capability and key. Mesh handles shard ownership and node selection, allowing systems to scale without relying on global process registries.

Example usage

Registering a process with a capability:

Mesh.register_capabilities([:game, :chat])

Routing a message to a virtual process by capability and key:

{:ok, pid, response} = Mesh.call(%Mesh.Request{
  module: MyApp.GameActor,
  id: "player_123",
  payload: %{action: "move"},
  capability: :game
})

Mesh also exposes simple functions for understanding cluster state:

Mesh.nodes_for(:game)     #=> [:node1@host, :node2@host]
Mesh.all_capabilities()   #=> [:game, :chat, :payment]

These helpers return lists of nodes that support a given capability or all capabilities registered in the cluster

Under the hood, Mesh computes a shard from the routing key, determines the owner node for that shard and capability, and delivers the message to the appropriate process — locally or remotely — without the caller needing to know where that process lives.

This makes Mesh particularly useful for systems that need:

  • Logical actors

  • Deterministic placement

  • Clear separation between routing logic and business logic

Project status

Mesh is new and under active development. The API is intentionally small, and we expect it to evolve as we continue integrating it into Spawn and gather feedback from real-world usage.

We’re sharing it early because we believe the underlying ideas may be useful beyond our own projects, especially for people building distributed systems on the BEAM who need more control over routing semantics.

Links

Documentation:
https://hexdocs.pm/mesh/Mesh.html

Hex package:

Spawn:

https://github.com/eigr/spawn

GitHub repository:

https://github.com/eigr/mesh

Feedback, questions, and contributions are very welcome.

— Eigr community

First 10 of 27 Posts Switch mode

garrison

garrison

Seems cool.

Btw, this is not consistent hashing. It’s just regular hash sharding. There is no ring; it will catastrophically reshard if a node is added.

You should use rendezvous hashing anyway. That is, if you need to worry about resharding at all (I’m not sure you do).

sleipnir

sleipnir OP

Good catch — you’re right about the terminology.

Mesh currently uses deterministic hash-based sharding, not classic consistent hashing with a ring. Shards are fixed and process are mapped to shards via phash2/2.

That said, this is a deliberate design choice aligned with our use case. Mesh routes messages to virtual processes, not persistent data partitions. Actors are ephemeral, lazily created, and can be safely recreated elsewhere. As a result, minimizing “resharding cost” is not a primary concern for us.

Nodes joining or leaving do not change shard assignment — only shard ownership. There is no data migration involved, and no state is coupled to shard placement.

Rendezvous hashing is a great tool and could be explored in the future, but for now the current approach gives us simpler reasoning, O(1) routing decisions, and predictable behavior, which fits well with Mesh and Spawn’s actor model.

Still, thanks for pointing this out — we should probably avoid calling it “consistent hashing” in the docs and be more precise about the terminology. We’re always open to evolving the routing strategy as new requirements emerge.

Asd

Asd

Did you just AI-generate the answer?

It is not predictable and it is not consistent. If you had shart = 2 and nodes were [:node0, :node2, :node3], you’d get :node3 returned, but then a :node1 joins and sets the same capability, and you’d get a :node2 returned, which is kinda the opposite of consistent and predictable.

sleipnir

sleipnir OP

No, I was just trying to be polite.

As I said before, perhaps the text implied that partitions should be stateful or stable, but this is not the use case. The state of a process is not coupled to the shard; it only serves as a routing mechanism. Obviously, this doesn’t work for all use cases. We don’t need consensus on the shard. Sorry if I wasn’t academic or precise enough in the text, we can improve. By the way, considering this is version 0.1.1, contributions are welcome, and I’m sure that if there’s a use case or even a bug, a PR would be very well received.

Asd

Asd

Haha, it’s alright. It’s just that every response which starts with “Good catch, you’re absolutely right” reads as an LLM output. My friend even has a t-shirt with Claude logo and this phrase on it :grin:

If you don’t need to be consistent, why do you even shard in the first place? Pick random, round-robin with counters, etc. It would be much faster then hash computation and (more importantly) would not give a false feeling of consistent routing

sleipnir

sleipnir OP

Haha, it’s alright. It’s just that every response which starts with “Good catch, you’re absolutely right” reads as an LLM output. My friend even has a t-shirt with Claude logo and this phrase on it :grin:

I understand. Although that wasn’t the case, I’ll learn more about the terms used by AI so I don’t repeat them anymore :wink:

It seems you assume that if there’s no strong consistency then sharding doesn’t make sense (I’m assuming you thought that). But that’s not the case, sharding ins’t just for consistency (predictability, local determinism, stability, and other things probably).

I didn’t want shared state, or inconsistent decisions from each node; if the cluster is stable, you would lose local determinism otherwise, but all nodes arrive at the same decision in this case (stable cluster) with shards.

Regarding performance, I’m very happy with the benchmarks we’ve done and we’ll work more on that over time. And of course, we can consider consistent and strong hashing as you assume, but for this version we’re okay with that.

But we can build together, I’d be happy to see a PR.

BartOtten

BartOtten

This is a brilliant concept which reminds me of Kubernetes. Having it in Elixir is tight. In that regard: is there a possibility to provide a negative-capability (taint)?

‘The system’ as the mesh or is there an orchestrating node.

And when a node is killed, are the processes restarted at other nodes?

krasenyp

krasenyp

I don’t think the term capability-based means what you think it means. A capability is an inforgeable token of authority. I don’t see such thing in Mesh. These atoms that you call capabilities are more like roles or groups, or identifiers.

sleipnir

sleipnir OP

is there a possibility to provide a negative-capability (taint)?

It would seem more like a node affinity than a taint.

And when a node is killed, are the processes restarted at other nodes?

When there is a new call to the process yes!

sleipnir

sleipnir OP

These atoms that you call capabilities are more like roles or groups, or identifiers.

Yes. They are tags, labels, whatever you want to call them, and they serve more as a group affinity for nodes, in the sense that you are addressing a process to a node only if it is part of that specified “group”.

Where Next?

Trending in Announcing Top

bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
387 14960 120
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
shahryarjb
The Chelekom project is a library of Phoenix and LiveView components generated via Mix tasks to fit developer needs seamlessly. One of i...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

Other Trending Topics Top

type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
bjorng
We want to introduce a new native datatype to Erlang: native records. Although replacing all tuple records with native records is not our...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New

We're in Beta

About us Mission Statement