Repository navigation
v2.0.0
2.0.0
A major rewrite of the core transport and operator experience. Upgrading requires re-pairing (see Breaking changes).
Breaking changes
- Dropped the
solaardependency.flow-server/flow-clientnow talk to/dev/hidraw*directly through a newlogitech_flow_kvm.hidpppackage that owns the HID++ wire protocol (Bolt and Unifying receivers), with no dependency beyond the stdlib. Solaar's actuator path was built for its interactive config UI and asserted when a device was unreachable — the normal steady state while the reconciler retries — so it couldn't be reused safely. - Devices are now identified by serial number instead of
hidrawpath. Paths aren't stable across reboots; serials are. If you have scripts or config pinning a specific device path, update them to use the serial shown bylist-devices. - Requires Python 3.10+.
- Regenerating the server's TLS certificate (e.g. after adding
--hostname) invalidates every already-paired client's cached copy. A freshly startedflow-clientre-pairs automatically; one already running does not and needs a restart.
New features
- Interactive TUI.
flow-server/flow-clientnow show a live status panel and scrolling log panel (via Textual) when run from a terminal, including connected-guest names on the server side. Pairing prompts moved into a proper modal instead of blocking on stdin, which no longer conflicts with the TUI owning the terminal. Non-interactive runs (e.g. under systemd) are unaffected and keep the plain-log fallback. - Real logging, with a rotating log file. Every message now goes through the standard logging pipeline and is always mirrored to a rotating log file under your platform's per-app log directory (via
platformdirs), so you can check what happened even after it's scrolled off-screen. - SSE-driven reconciliation loop. Replaced the old disconnect-triggered, guess-the-new-host-and-sleep dance with a shared
Reconcilerthat retries on positive evidence (a real connect notification) until convergence, fed by a new/eventsSSE endpoint that pushes the leader's current host to every client. Transient failures (e.g. a device unreachable mid-roam) no longer kill the reconciler loop. --hostname/-Hflag forflow-server. Declare the name(s) clients will reach the server as (mDNS.local/.lan, etc.) so the generated certificate's SAN list actually matches, instead of guessing viasocket.gethostname().- Pairing requests are now serialized, so two concurrent attempts no longer interleave prompts on a shared console.
Fixes
- Fixed missing
CA:TRUEbasic constraint on the self-signed certificate, which modern OpenSSL requires to use it as a trust anchor — this was silently breakingflow-client's TLS verification. - Fixed stale README examples showing device paths where matching has always been by serial number.
- Restored werkzeug's startup banner (the line telling you the server's bind address), which had been accidentally silenced along with its per-request access log.
- Clipboard data is now explicitly encoded as UTF-8 bytes.
- Stopped enumerating theoretical devices past a receiver's actual maximum, and sped up enumeration by assuming lower device IDs are used first.
Packaging & tooling
- Migrated from
setup.py/setup.cfg/MANIFEST.into PEP 621pyproject.toml(hatchling). - Replaced
appdirs→platformdirs,netifaces→psutil,pyOpenSSL→cryptography; dropped the unusedhid_parserdependency. - Moved to Flask 3,
rich13+, and switched linting/formatting from black/flake8/isort toruff. - CI now runs on a uv-based matrix across Python 3.10–3.14.
- Added a real test suite (hidpp protocol/discovery/receiver, reconciler, SSE, logging setup, flow-server/flow-client, clipboard/cert utilities).