222 lines
8.8 KiB
Markdown
222 lines
8.8 KiB
Markdown
# Changelog
|
||
|
||
## [3.0.1] - 2026-05-11
|
||
|
||
### Fixed
|
||
|
||
- **DHT `PublishFailing` no longer wedges the swarm for minutes at a
|
||
time.** Pulls in `iroh-gossip-rendezvous` 0.2.1, which retries
|
||
failed publishes on a 15 s cadence (default) and bypasses the
|
||
`1/(n+1)` write-probability gate while the DHT is unhealthy. Prior
|
||
to this, a single failed publish parked the bridge in
|
||
`PublishFailing` for at least `write_period` (5 min) and on
|
||
average `(n + 1) × write_period` — half an hour with a populated
|
||
active view — before the next retry attempt. Recovery is now
|
||
bounded by `failure_retry_period × consecutive_failures` and
|
||
flips back to `Ready` the instant a write lands.
|
||
|
||
## [3.0.0] - 2026-05-11
|
||
|
||
### Breaking
|
||
|
||
- **`iroh` bumped 0.98 → `=1.0.0-rc.0`** and **`iroh-gossip` bumped
|
||
0.98 → 0.99**. The bridge is now built on iroh's first 1.0 release
|
||
candidate. iroh-gossip 0.99 hard-pins `iroh = "=1.0.0-rc.0"`, so the
|
||
bridge does too until iroh ships a final 1.0 — once it does, this
|
||
pin will relax to plain `"1"`. Any downstream consumer that pulls
|
||
iroh transitively must move in lockstep.
|
||
- **`iroh-gossip-rendezvous` bumped 0.1 → 0.2** (now consumed from
|
||
crates.io; the local path-dep is gone). The 0.2 release of the
|
||
rendezvous crate carries the same iroh 1.0-rc.0 bump and pins
|
||
`ed25519-dalek = "=3.0.0-pre.7"` to match iroh-base's pre-release.
|
||
|
||
### Changed
|
||
|
||
- `donglora-client` resolves to 1.1.1 (was 1.0.x). Adds the
|
||
`connect()`-time auto-clamp of `tx_power_dbm` against
|
||
`GET_INFO.tx_power_min/max` and pre-flight rejection of
|
||
out-of-range `freq_hz` / `sf` / `bw` with
|
||
`ClientError::ConfigNotSupported` — both surface as ordinary
|
||
bridge connection errors today.
|
||
- `Cargo.lock` refreshed end-to-end: `mainline` 6.0 → 6.1.2, plus
|
||
the usual tokio/rustls/hyper patch sweep.
|
||
|
||
### Notes
|
||
|
||
- **Pre-v2 config files were silently mis-loaded.** A stale
|
||
`~/.config/donglora-bridge/config.toml` from v0.x / v1.x — with
|
||
`sync_word = 13380` (`0x3444`, the old LoRaWAN-public default),
|
||
`tx_power = "22"` (old string field), or `cad = true` (removed in
|
||
v2) — would deserialize without error: serde silently ignored the
|
||
unknown `tx_power` / `cad` fields and the bridge fell back to its
|
||
per-field defaults, producing a "requested config" that didn't
|
||
match what the user had ever entered. On a shared mux the TUI
|
||
flagged this as a `!` against the active config. Re-run
|
||
`donglora-bridge config` (or update the file by hand) if your
|
||
config still has any of `tx_power`, `cad`, or
|
||
`sync_word = 13380`. A future minor will tighten the loader so
|
||
this fails loudly instead.
|
||
|
||
## [2.0.1] - 2026-04-25
|
||
|
||
### Fixed
|
||
|
||
- **TUI quit no longer hangs on a misleading "updating DHT" screen.**
|
||
The 2.0.0 migration to `iroh-gossip-rendezvous` removed the
|
||
synchronous mainline-DHT publish that the old `Gossip::shutdown()`
|
||
performed (rendezvous entries age out instead), but the TUI flow
|
||
was unchanged: pressing `q` drew an "updating DHT…" overlay,
|
||
awaited `swarm.shutdown()`, and only then restored the terminal —
|
||
so the user sat on the stale message for as long as
|
||
`endpoint.close()` took to flush in-flight connections. Restore
|
||
the terminal the moment `q` is pressed, drop the overlay, and
|
||
cap the graceful-shutdown wait at 2 s so a slow endpoint close
|
||
can't stall the process.
|
||
|
||
## [2.0.0] - 2026-04-23
|
||
|
||
### Breaking
|
||
|
||
- **Partition-healing / DHT-rendezvous extracted to its own crate.**
|
||
The 700+ lines of gossip-partition-healing logic in `src/gossip.rs`
|
||
are now the `iroh-gossip-rendezvous` crate on crates.io (pinned
|
||
here at `^0.1`). Any downstream code that reached into
|
||
`bridge::gossip::*` for partition types needs to import from
|
||
`iroh_gossip_rendezvous::*` instead. The normative protocol spec
|
||
moved with the code — `dht-partition-healing.md` at the bridge
|
||
root is now a pointer to the new crate's `PROTOCOL.md`.
|
||
- **`iroh` and `iroh-gossip` bumped 0.97 → 0.98** (major; carries
|
||
iroh's own breaking changes through to any bridge consumers that
|
||
import from those crates transitively).
|
||
|
||
### Changed
|
||
|
||
- Direct deps `mainline`, `sha2`, `zeroize` removed — all now
|
||
reachable transitively via `iroh-gossip-rendezvous`. Closure is
|
||
smaller and the rendezvous concern lives in exactly one place.
|
||
- `donglora-client` version spec loosened `"1.0.0"` → `"1"`. Patch
|
||
and minor releases of the client propagate on the next
|
||
`cargo build` / `cargo install` without a bridge re-publish;
|
||
already-released `donglora-client 1.0.1` (CH340K USB bridge
|
||
support) is picked up automatically.
|
||
|
||
## [1.0.1] - 2026-04-22
|
||
|
||
### Security
|
||
|
||
- Bump both pinned `rand` versions past [RUSTSEC-2026-0097][adv] (`rand`
|
||
is unsound with a custom `log::Log` impl that calls `rand::rng()` at
|
||
trace/warn level and hits the 64 KB reseed path):
|
||
- `rand 0.9.2` → `0.9.4` (fix `>= 0.9.3`).
|
||
- `rand 0.8.5` → `0.8.6` (fix `>= 0.8.6`).
|
||
|
||
The advisory is only reachable under a very narrow set of conditions
|
||
(custom logger invoking `rand::rng()` during a reseed while emitting
|
||
trace-level logs, or warn-level with a failed `getrandom`). We are
|
||
not known to do that, but shipping the upgrade is the right call.
|
||
|
||
[adv]: https://rustsec.org/advisories/RUSTSEC-2026-0097
|
||
|
||
## [1.0.0] - 2026-04-22
|
||
|
||
### Breaking
|
||
|
||
- **Upgraded to `donglora-client` 1.0.0-alpha.1 (DongLoRa Protocol v2 wire protocol).**
|
||
Wire-incompatible with 0.x firmware. Connect/config/TX/RX all go through
|
||
the new async `Dongle` API.
|
||
- **Radio loop is now a tokio task**, not a dedicated `std::thread`.
|
||
Everything is async end-to-end.
|
||
- **`[radio]` config schema changes:**
|
||
- `tx_power` renamed to `tx_power_dbm` and is now a plain `i8` (no
|
||
`"max"` keyword — v1.0 exposes a concrete dBm range via `GET_INFO`).
|
||
- `cad` field removed — CAD is a per-TX flag in v1.0, not a config
|
||
property. The `[tx]` section's `skip_cad` (default false) controls it.
|
||
- Default `sync_word` changed `0x3444` → `0x1424` to match the
|
||
`SX126x` register encoding of `MeshCore`'s private sync byte
|
||
(`RADIOLIB_SX126X_SYNC_WORD_PRIVATE`).
|
||
|
||
### Added
|
||
|
||
- **TX retry with backoff (new `[tx]` config section).** Every TX now
|
||
runs through `Dongle::tx_with_retry`, which retries `TX_DONE(CHANNEL_BUSY)`
|
||
and `ERR(EBUSY)` per spec `PROTOCOL.md §6.10` / §C.5.5. Defaults: 3
|
||
attempts, randomized 20–100 ms backoff, doubling up to 500 ms cap,
|
||
5 s per-attempt timeout. Configurable via `[tx]` keys `max_attempts`,
|
||
`backoff_min_ms`, `backoff_max_ms`, `backoff_cap_ms`,
|
||
`backoff_multiplier`, `per_attempt_timeout_secs`.
|
||
- **TUI packet log shows retry state.** In-flight retries render with a
|
||
`↻` marker in yellow and an `N/M` attempt counter. Terminal TX failures
|
||
render in red with `✗`. Post-retry success shows the final attempt count
|
||
(`OK 2/3`).
|
||
- **Stats: `tx_retries` and `tx_failures` counters.** Lifetime totals,
|
||
surfaced in the `StatsSnapshot`.
|
||
- **Measured airtime** from `TX_DONE` is now reported instead of a
|
||
theoretical estimate, feeding the rate limiter with the real on-air
|
||
duration.
|
||
|
||
### Removed
|
||
|
||
- The explicit 2-second liveness ping loop in the radio thread. The
|
||
`Dongle` now runs a 500 ms keepalive task internally, matching spec
|
||
§3.4's 1 s inactivity window with 2× margin.
|
||
|
||
## [0.3.1] - 2026-04-07
|
||
|
||
### Fixed
|
||
|
||
- **Sticky mux reconnect.** Once the bridge connects via mux, all subsequent
|
||
reconnects use mux-only mode (`connect_mux_auto`). Previously, each reconnect
|
||
re-ran the full mux→USB fallback chain, which could steal the serial port
|
||
from the mux during a brief disconnect.
|
||
|
||
## [0.3.0] - 2026-04-07
|
||
|
||
### Changed
|
||
|
||
- Upgraded to `donglora-client` 0.2: all connections now auto-validate via
|
||
ping-on-connect, rejecting non-DongLoRa serial devices within 200ms.
|
||
- Simplified connection logic — removed manual mux/serial fallback and manual
|
||
ping; `donglora_client::connect()` handles the full fallback chain and
|
||
validation internally.
|
||
- Config negotiation detects mux vs serial via transport type instead of
|
||
string comparison.
|
||
|
||
### Fixed
|
||
|
||
- Repository and homepage URLs now point to `github.com/donglora/bridge`
|
||
(was `swaits/donglora-bridge`).
|
||
- README links updated to `donglora` GitHub org.
|
||
|
||
## [0.2.2] - 2026-04-06
|
||
|
||
### Fixed
|
||
|
||
- TUI now fits in 80-column terminals (was 86).
|
||
|
||
## [0.2.1] - 2026-04-06
|
||
|
||
### Fixed
|
||
|
||
- CI: removed stale sibling-clone step, added `libudev-dev` dependency,
|
||
upgraded `actions/checkout` to v5 (Node.js 24).
|
||
|
||
## [0.2.0] - 2026-04-06
|
||
|
||
### Added
|
||
|
||
- Auto-detect connection mode: when no `port` is configured, the bridge now
|
||
tries the mux daemon first and falls back to direct USB serial
|
||
(`find_port()`) if the mux is unavailable. Previously it would retry the mux
|
||
forever.
|
||
|
||
### Changed
|
||
|
||
- Config negotiation on direct serial connections now pushes `SetConfig` with
|
||
the desired radio config when it differs from the dongle's current config.
|
||
On mux connections, the existing behavior (accept the mux's config) is
|
||
unchanged.
|
||
|
||
## [0.1.0] - 2026-04-06
|
||
|
||
Initial release.
|