# Changelog
All notable changes to this project are documented here. The format
follows [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).
## [Unreleased]
## [0.1.2] - 2026-05-08
### Documentation
- **Operational considerations section** added to the README,
documenting two production footguns:
- Sender restart producing a new `{ip, port}` peer identity
from the Listener's perspective (because Sender uses
ephemeral source ports). Recommends `:node_resolver` as the
standard production setup.
- DNS resolution cost and failure modes in Sender. Recommends
pre-resolved IP tuples for deployments with unreliable DNS.
- Listener moduledoc reframes `:node_resolver` as the recommended
production setup, not just a "static topology" option.
- Sender moduledoc expands the DNS resolution note.
No behaviour change.
## [0.1.1] - 2026-05-08
### Changed
- **Listener flow control.** `PhiAccrualUdp.Listener` now opens its
UDP socket with `active: N` (default `N=100`, configurable via the
`:active_count` option) instead of `active: true`. Re-arms on
`:udp_passive`. This bounds the per-burst mailbox growth under
packet floods.
### Added
- Telemetry event `[:phi_accrual_udp, :listener, :passive]`, emitted
each time the listener re-arms after consuming `active_count`
packets. Useful for observing ingress saturation.
## [0.1.0] - 2026-05-07
Initial public release. **Alpha** — public API and wire format may
change before `v1.0` based on real-deployment feedback.
### Added
- **Wire format v1** (`PhiAccrualUdp.Packet`) — 12-byte fixed format
with magic `0xCEA6`, version `0x01`, reserved flags byte (must be
zero in v1), and 64-bit unsigned millisecond timestamp.
- **UDP listener** (`PhiAccrualUdp.Listener`) — opens a UDP socket on
a configurable port, decodes incoming packets, calls
`PhiAccrual.observe/2` with local monotonic receipt time. Decode
failures emit `[:phi_accrual_udp, :decode, :error]` telemetry with
reason classification.
- **Periodic UDP sender** (`PhiAccrualUdp.Sender`) — sends heartbeat
packets to a list of `{host, port}` targets at a configurable
interval. Configurable timestamp source.
- **Custom node resolution** — listener accepts a `:node_resolver`
function mapping `(ip, port)` to user-defined node identifiers.
Default: `{ip, port}` tuple.
- **Telemetry schema** — `[:listener, :started]`, `[:sample, :received]`,
`[:decode, :error]`, `[:sender, :started]`, `[:sender, :tick]`.
### Notes
- Wire format and telemetry schema are **not yet committed**. Both
may change before `v1.0`. Magic/version/flags structure is
deliberately chosen to permit format evolution without breaking
on-the-wire compatibility for v1 senders.
- Receiver-driven clock discipline: the EWMA uses local monotonic
receipt time, never the packet timestamp. This preserves
`phi_accrual`'s contract that cross-node timestamps are
meaningless.