All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.1.0 - 2026-08-03
Four mesh-daemon strategies and their shared discovery pipeline.
Added
Cluster.Strategy.TailscaleLocal— peer discovery viatailscale status --json, working with Tailscale and with Headscale-based control servers, and needing no API keyCluster.Strategy.LolipopZTL— peer discovery viaztlctl statusCluster.Strategy.Netbird— peer discovery vianetbird status --jsonCluster.Strategy.WireGuard— peer discovery viawg show <if> dumpfor plain WireGuard meshes, selecting members by IPv4 CIDR and treating peers with a recent handshake as onlinecidrmember selection (LibclusterMesh.CIDR) alongside hostname prefixesLibclusterMesh.Runner, a behaviour for running the mesh CLI, with a timeout-guarded port-based default that reports a missing binary or a hung daemon rather than raising, and signals a command outliving its timeout so that a wedged daemon cannot leave one behind on every poll- Config validated at startup by
LibclusterMesh.Config.new!/3
Notes
- Publishing is triggered by pushing a
v*tag. The workflow refuses a tag whose version disagrees withmix.exs, or one with no section in this file, because a version on hex cannot be corrected or reused. - A config key a strategy does not support raises
ArgumentErrorat startup rather than being ignored. This is the failure that is hardest to diagnose otherwise: a mistyped or misplaced key means no peer ever matches, so the cluster never forms and nothing says why.socketis part of that — onlyCluster.Strategy.LolipopZTLtakes one. - A daemon that answers with the right key and the wrong shape is an error,
not an empty peer list. A strategy that keeps polling and finds nobody looks
exactly like a mesh with nothing on it, so each parser guards its "no peers"
clause on the key being absent rather than letting it catch everything —
%{}matches every map in Elixir.