The 1.0 mux, running on the Protocol v2 wire format and full §13 multi-client semantics.
* Tag-aware dispatch. Each inbound client frame is re-tagged with a mux-allocated device tag; responses are routed back to the original client using the tag mapping. The single-oneshot response slot is gone; multiple clients can have commands in flight concurrently.
* Full §13.2 multi-client SET_CONFIG semantics. Tracks the (client, modulation) lock owner and synthesizes OK(APPLIED/MINE), OK(ALREADY_MATCHED/OTHER), or OK(LOCKED_MISMATCH/OTHER) based on lock ownership and parameter match. Lock releases automatically on client disconnect.
* TX loopback fan-out (§13.4). On TX_DONE(TRANSMITTED) the mux synthesizes an RX frame with origin = LocalLoopback and sends it to every connected client except the transmitter.
* Async ERR(EFRAME) fan-out. If the dongle emits async framing errors (tag = 0), they are broadcast to all clients rather than swallowed.
* TagMapper with skip-zero allocation and per-client bulk drop on disconnect.
* Per-client TX cache on ClientSession so the loopback fan-out does not need the sender to re-buffer.
* 500 ms keepalive cadence matching PROTOCOL.md §3.4 inactivity window (replacing the old 5 s loop).
UART boards (CP2102 bridge) keep the serial port alive across ESP32 resets,
so the mux never detected the board rebooting. Now the writer sends a
keepalive Ping every 5s of idle and declares the dongle lost after a
2s timeout.
On reconnect, saved radio config and RX state are re-sent to the dongle
so connected clients continue working without re-establishing state.
The mux now acquires an flock-based exclusive lock on the serial port
before opening it. This prevents bridges or other processes from directly
opening the same device while the mux owns it. The lock is held for the
duration of the dongle session and auto-released on disconnect or crash.