Syn replaces OTP's global registry with an eventually consistent one
A scalable global Process Registry and Process Group manager for Erlang and Elixir.
At a glance
- What is it?
- Syn is an Erlang and Elixir library that manages a global process registry and process groups across a cluster, and it is a replacement for the OTP global and pg modules that trades strict consistency for availability and write speed, adds metadata to registrations, and survives net splits. It is MIT licensed and published on Hex.
- Who is it for?
- Syn suits an Erlang or Elixir team whose cluster is large enough that OTP's global module registration path has become the bottleneck, or whose application registers keys that are already unique so that a consistency trade is not needed, which is the IoT case the library was designed around. It is the wrong tool if you need a single authoritative answer for a key that two nodes may register simultaneously, because strong eventual consistency means both can succeed.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Erlang, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What it replaces and why
Syn, short for synonym, is a scalable global Process Registry and Process Group manager for Erlang and Elixir, able to automatically manage dynamic clusters with addition and removal of nodes and to recover from net splits.
It is a replacement for Erlang/OTP's global and pg modules, and the README lists three differences from those OTP implementations.
OTP's global module chooses Consistency over Availability, so it can become difficult to scale when registration rates are elevated and the cluster becomes larger, and if eventual consistency is acceptable in your case, Syn can considerably increase the registry's performance. Syn allows attaching metadata to every process, and that metadata is also synchronized across the cluster. And Syn implements cluster-wide callbacks on the main events, which are also properly triggered after net splits.
The third point is the one that is easy to overlook. A callback that fires on a net split means the system has to know what happened while the cluster was partitioned, and firing it twice or firing it with the wrong data is a classic source of duplicate side effects. The README says these are properly triggered after net splits, which is a claim about correctness under partition, not just about speed.
The library is MIT licensed, written in Erlang, and published on Hex as syn, with the documentation on hexdocs. The last push was on 2026-10-18 and the repository is not archived, though there are no GitHub releases.
Registry, groups and the two data models
The Introduction section explains the two abstractions separately, and the distinction is worth holding on to because the two features have different shapes.
A global Process Registry allows registering a process on all the nodes of a cluster with a single Key. The README offers an analogy: consider this the process equivalent of a DNS server, in the same way you can retrieve an IP address from a domain name, you can retrieve a process from its Key. The typical use case is registering on a system a process that handles a physical device, using its serial number.
A global Process Group is a named group containing many processes, possibly running on different nodes. With the group Name you can retrieve on any cluster node the list of these processes, or publish a message to all of them, which allows for publish and subscribe patterns. The typical use case is a chatroom.
The feature list then enumerates the rest. A process is uniquely identified with a Key across all nodes, and a group is uniquely identified with a Name. Any term can be used as a Key and a Name, which is a genuinely useful loosening compared with OTP's string keys. PubSub can publish to all members of a group, globally on all the cluster or locally on a single node. Subclusters by using Scopes allow great scalability. Clusters are dynamically sized, with node addition and removal handled automatically. Net splits are resolved automatically. Writes are fast. Callbacks are configurable. And processes are automatically monitored and removed from the registry and groups if they die.
That last one removes a class of bug that is easy to create and hard to find. In a hand built registry, a crashed process leaves a stale entry, and the stale entry is discovered when a caller tries to send to a dead pid.
The consistency trade, and why IoT keys make it safe
The Notes section is the argument for the design, and it is unusually direct about the trade being made.
In any distributed system you face a consistency challenge, which is often resolved by having one master arbiter performing all write operations, chosen with a leader election mechanism, or through atomic transactions.
Syn was born for applications of the IoT field. In that context, the keys used to identify a process are often the physical object's unique identifier, such as its serial or MAC address, and are therefore already defined and unique before hitting the system. The consistency challenge is less of a problem in this case, since the likelihood of concurrent incoming requests that would register processes with the same Key is extremely low and, in most cases, acceptable.
In addition, write speeds were a determining factor in the architecture.
Therefore, Availability has been chosen over Consistency, and Syn implements strong eventual consistency.
The distinction between availability and consistency is the crux. If two nodes simultaneously register the same key, Syn does not elect a winner and does not block one of them. Both writes succeed, and the conflict is resolved later as the cluster converges. That is the right choice when the same key is genuinely being registered by two different things, because one of them has to lose either way. It is the wrong choice when the key means one specific thing and the application needs to know which one won, and it is a real behaviour change if you are replacing OTP's global module in an application that assumed a single answer.
Strong eventual consistency, as the term is used here, means the system guarantees convergence: once the cluster is whole again, all nodes agree. It does not mean a read immediately after a write on another node sees that write, and it does not mean the resolution order is defined for you.
Installing it in Elixir and Erlang
For Elixir, the dependency is added to the deps list:
defp deps do
[{:syn, "~> 3.4"}]
endFor Erlang with rebar3, you add syn to the deps list in rebar.config, either from git with a tag:
{deps, [
{syn, {git, "git://github.com/ostinelli/syn.git", {tag, "3.4.2"}}}
]}.Or from Hex.pm using the rebar3_hex plugin:
{deps, [
{syn, "3.4.2"}
]}.Then the application has to be started with syn, by adding it to the applications list in the .app file:
{application, my_app, [
%% ...
{applications, [
kernel,
stdlib,
sasl,
syn,
%% ...
]},
%% ...
]}.The `~> 3.4` requirement for Elixir and the pinned 3.4.2 tag for Erlang are the same version, and the ordering in the applications list matters because syn has to be running before anything that registers into it.
The repository is a rebar3 project, with rebar.config and rebar.lock at the root alongside src/ and test/ directories, and a Makefile that wraps rebar3 for the common tasks: all and compile_test run rebar3 compile in the normal and test profiles, dialyzer runs rebar3 dialyzer, and test and proper run the Common Test suites with ct_run against the test directory, with an optional suite variable to select a single suite.
A first session with the registry
The quickstart in the README is an IEx session, and it shows the shape of the API in a way a description would not.
For Elixir:
iex> :syn.add_node_to_scopes([:users])
:ok
iex> pid = self()
#PID<0.105.0>
iex> :syn.register(:users, "hedy", pid)
:ok
iex> :syn.lookup(:users, "hedy")
{#PID<0.105.0>,:undefined}The first call matters and is easy to miss. add_node_to_scopes puts the local node into a scope, and until a node is in a scope it is not part of the cluster's registry at all. Scopes are the mechanism behind the subcluster claim: two parts of a system that should not see each other's registrations live in different scopes.
The lookup result is a two element tuple of the pid and the metadata. With no metadata it is :undefined, and registering again with metadata attaches it:
iex> :syn.register(:users, "hedy", pid, [city: "Milan"])
:ok
iex> :syn.lookup(:users, "hedy")
{#PID<0.105.0>,[city: "Milan"]}And registry_count gives the number of entries in the scope, which returns 1 in the example.
The Erlang session in the README is the same sequence with the atom :users replaced by the atom users and the keyword list replaced by a list of tuples, so [{city, "Milan"}] rather than [city: "Milan"]. The API is one library rather than two, which is the point of supporting both languages with the same functions.
Groups, publish and the message flow
The process group quickstart follows the same pattern and adds the publish operation.
iex> :syn.join(:users, {:italy, :lombardy}, pid)
:ok
iex> :syn.members(:users, {:italy, :lombardy})
[{#PID<0.88.0>,:undefined}]
iex> :syn.is_member(:users, {:italy, :lombardy}, pid)
true
iex> :syn.publish(:users, {:italy, :lombardy}, "hello lombardy!")
{:ok,1}The publish return value is the interesting detail: {:ok,1} is the number of members that received the message, so a publisher knows how far the message went. In the example the receiving process is the shell itself, which is why the following flush() prints Shell got "hello lombardy!".
The group name is a tuple, {:italy, :lombardy}, which is a good illustration of the any term as a Name claim. A hierarchical group name expressed as a tuple is readable and comparable, where a flattened string would need a separator convention and an escaping rule.
The members list carries metadata per member, in the same shape as the registry lookup, so a member's metadata travels with it and is synchronized across the cluster.
Since publishing is a PubSub mechanism, the same call works globally on all the cluster or locally on a single node, and the local variant is what makes scoped subclusters coherent: a process in the users scope publishes to users members, and a process in the other scope does not see them.
Where Syn fits, and where it does not
The honest comparison is with OTP's own modules and with the alternative of writing your own registry, and the difference is entirely about which consistency model you want.
Against OTP's global, the difference is the consistency choice. global serialises writes through a single arbiter so that every node has the same answer at the same time, and the cost of that is that registration throughput does not scale and the cluster can become the bottleneck. Syn gives up the guarantee of a single answer at a given instant in exchange for writes that do not queue behind a leader. If your application genuinely needs one authoritative mapping, global remains the right answer and Syn is not a drop-in replacement.
Against pg, the difference is the same plus the metadata. pg is a purely local group manager with a cluster-wide view layered on top, and Syn makes the group membership and its metadata cluster-wide and net-split aware.
The alternative of writing your own is worth considering for a small system, and Syn's own documentation pushes you that way, since it points to OPTIONS.md for the configuration surface and INTERNALS.md for the consistency model, which is the documentation you need to make that judgement.
The limits are structural rather than accidental. Strong eventual consistency means concurrent registrations of the same key both succeed, and if your keys are not already unique, the application has to deal with which one won. The IoT framing in the Notes section is an assumption about your data, not just a use case, and the README is clear that it is acceptable only because concurrent same-key registrations are extremely unlikely.
There is also a dependency question. Syn is a library you add to your application's dependencies and start before the rest of the system, as the .app file example shows. It is not infrastructure you can run separately, and there is no persistent store involved, so the registry lives entirely in the cluster's memory and its state is rebuilt from the nodes that are present. That is fast and simple, and it also means the registry is empty after a full cluster restart until processes re-register.
Editorial conclusion
Syn suits an Erlang or Elixir team whose cluster is large enough that OTP's global module registration path has become the bottleneck, or whose application registers keys that are already unique so that a consistency trade is not needed, which is the IoT case the library was designed around. It is the wrong tool if you need a single authoritative answer for a key that two nodes may register simultaneously, because strong eventual consistency means both can succeed. Before adopting it, read OPTIONS.md for the configuration surface and INTERNALS.md for the consistency model, and check whether your keys really are unique before registration as the library assumes.
Frequently asked questions
What does Syn replace in Erlang/OTP?
It is a replacement for the OTP global and pg modules. The stated differences are that OTP's global chooses Consistency over Availability and so can be difficult to scale at high registration rates, that Syn allows attaching metadata to every process which is also synchronized across the cluster, and that Syn implements cluster-wide callbacks on the main events which are properly triggered after net splits.
How do I add Syn to an Elixir project?
Add {:syn, "~> 3.4"} to the deps list. For Erlang with rebar3, add syn to the deps in rebar.config either from git with the tag 3.4.2 or from Hex.pm using the rebar3_hex plugin, and then add syn to the applications list in your .app file so it is started with your application.
What is the consistency model of Syn?
Syn chooses Availability over Consistency and implements strong eventual consistency. The README explains that concurrent registrations of the same key both succeed, and that this is acceptable because Syn was designed for IoT applications where keys are physical identifiers such as serial or MAC numbers that are already unique before reaching the system.
How do scopes work in Syn?
A node has to be added to a scope before it participates in that scope's registry, for example with :syn.add_node_to_scopes([:users]). Scopes are what allow subclusters, since registrations in one scope are not visible in another, and the same scope must be set on every node that should take part.
Can I attach metadata to a registered process in Syn?
Yes. You register with a metadata list, for example :syn.register(:users, "hedy", pid, [city: "Milan"]), and a lookup then returns the pid together with that metadata, which is synchronized across the cluster along with the registration itself.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ostinelli-syn)Was this useful?