# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.1] - 2026-08-03

### Fixed

- The install snippets name the package rather than the git repository they
  were written against before it was published

## [0.1.0] - 2026-08-03

Four mesh-daemon strategies and their shared discovery pipeline.

### Added

- `Cluster.Strategy.TailscaleLocal` — peer discovery via
  `tailscale status --json`, working with Tailscale and with Headscale-based
  control servers, and needing no API key
- `Cluster.Strategy.LolipopZTL` — peer discovery via `ztlctl status`
- `Cluster.Strategy.Netbird` — peer discovery via `netbird status --json`
- `Cluster.Strategy.WireGuard` — peer discovery via `wg show <if> dump` for
  plain WireGuard meshes, selecting members by IPv4 CIDR and treating peers
  with a recent handshake as online
- `cidr` member selection (`LibclusterMesh.CIDR`) alongside hostname prefixes
- `LibclusterMesh.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 with `mix.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 `ArgumentError` at 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. `socket` is part of that — only
  `Cluster.Strategy.LolipopZTL` takes 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.

[0.1.1]: https://github.com/kentaro/libcluster_mesh/releases/tag/v0.1.1
[0.1.0]: https://github.com/kentaro/libcluster_mesh/releases/tag/v0.1.0
