Merge maint into master after v0.2.1 release

Uses the 'ours' merge strategy to keep all of master's tree
verbatim with one carve-out: the v0.2.1 release notes archive at
docs/releases/release-notes-v0.2.1.md is retained as a permanent
docs artifact.

Discarded from maint's release-prep commit: Cargo.toml / Cargo.lock
version bumps (master stays on v0.3.0-dev), README v0.2.1 badge and
prose, and the RELEASE-NOTES.md root mirror (master's root mirror
will eventually carry the v0.3.0 release notes when v0.3.0 ships
from this line).

CHANGELOG: the v0.2.1 release section is integrated between
[Unreleased] (v0.3.0-dev work) and the [0.2.0] historical section.
Items that landed on maint during the v0.2.1 cycle are removed
from [Unreleased] so [0.2.1] is the canonical home for those
changes (Linux release artifact and AUR workflows; bloom fill-ratio
validation; seven maint-line bug fixes).
This commit is contained in:
Johnathan Corgan
2026-05-11 18:30:11 +00:00
2 changed files with 198 additions and 38 deletions
+57 -38
View File
@@ -204,9 +204,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Packaging and Deployment
- Linux release artifact workflow: builds x86_64 and aarch64 tarballs
and `.deb` packages on `v*` tag push, with SHA-256 checksums
- AUR publish workflow for tagged stable releases
- Arch Linux AUR packaging for `fips` (release) and `fips-git`
(development) packages with sysusers.d/tmpfiles.d integration
([#21](https://github.com/jmcorgan/fips/pull/21),
@@ -352,16 +349,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(`backoff_base_secs`/`backoff_max_secs` now both `0`); operators
with chatty apps generating repeat lookups against unreachable
destinations can opt back in
- Validate bloom filter fill ratio on FilterAnnounce ingress.
Inbound FilterAnnounce messages whose derived false-positive
rate exceeds `node.bloom.max_inbound_fpr` (new config field,
default 0.05) are rejected silently on the wire, logged at WARN,
and counted in a new `bloom.fill_exceeded` counter. A
rate-limited WARN also fires if our own outgoing filter's FPR
exceeds the cap. `BloomFilter::estimated_count` now takes
`max_fpr` and returns `Option<f64>`, returning `None` for
saturated filters; this propagates through `compute_mesh_size`
into `estimated_mesh_size` (already `Option<u64>`)
- The `docs/` tree is reorganised so readers can find content by
what they're trying to do: tutorials for new users, how-to guides
for specific tasks, reference material for configuration and
@@ -583,22 +570,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
matching the Tor `HiddenServicePort` convention) controls the
advertised port; operators with non-default virtual ports can
override.
- Control socket path detection in fipsctl and fipstop now checks for
the `/run/fips/` directory instead of the socket file inside it, so
users not yet in the `fips` group get a clear "Permission denied"
error instead of a misleading "No such file" fallback to
`$XDG_RUNTIME_DIR` ([#30](https://github.com/jmcorgan/fips/issues/30),
reported by [@Sebastix](https://github.com/Sebastix))
- OpenWrt ipk build excluded BLE feature that requires D-Bus, which is
unavailable on OpenWrt targets
- IPv6 routing policy rule added at TUN setup to protect `fd00::/8`
from interception by Tailscale's table 52 default route
- Bloom filter routing no longer swallows traffic when no bloom
candidate is strictly closer than the current node. `find_next_hop`
now falls through to greedy tree routing in that case instead of
returning `NoRoute`, which previously caused dropped packets in
topologies where the tree parent was closer but not a bloom
candidate
- TCP-over-FIPS reliability on mesh paths with mixed transport
MTUs (e.g. a UDP-1280 hop in the picker set) improved. Three
interlocking changes: `Node::transport_mtu()` is now deterministic
@@ -636,6 +607,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
the cooldown window are silent. New `protocol_mismatch_cooldown_secs`
config field under `node.discovery.nostr` (default 86400 = 24h),
separate from the transient-failure `extended_cooldown_secs`.
- `fipstop` now uses `ratatui::try_init()` instead of `ratatui::init()`,
so terminal initialization failures (e.g. Docker on macOS Sequoia,
or environments without a usable tty) produce a clean error message
instead of a hard crash
- Spanning-tree updates that change only the internal path between
root and leaf — without changing the root or the depth — now
propagate to leaves correctly. Previously a leaf could continue
routing against a stale internal path until the parent or depth
also changed.
## [0.2.1] - 2026-05-11
### Added
- Linux release artifact workflow: builds x86_64 and aarch64 tarballs
and `.deb` packages on `v*` tag push, with SHA-256 checksums
- AUR publish workflow for tagged stable releases
### Changed
- Validate bloom filter fill ratio on FilterAnnounce ingress.
Inbound FilterAnnounce messages whose derived false-positive
rate exceeds `node.bloom.max_inbound_fpr` (new config field,
default 0.05) are rejected silently on the wire, logged at WARN,
and counted in a new `bloom.fill_exceeded` counter. A
rate-limited WARN also fires if our own outgoing filter's FPR
exceeds the cap. `BloomFilter::estimated_count` now takes
`max_fpr` and returns `Option<f64>`, returning `None` for
saturated filters; this propagates through `compute_mesh_size`
into `estimated_mesh_size` (already `Option<u64>`)
### Fixed
- Control socket path detection in fipsctl and fipstop now checks for
the `/run/fips/` directory instead of the socket file inside it, so
users not yet in the `fips` group get a clear "Permission denied"
error instead of a misleading "No such file" fallback to
`$XDG_RUNTIME_DIR` ([#30](https://github.com/jmcorgan/fips/issues/30),
reported by [@Sebastix](https://github.com/Sebastix))
- OpenWrt ipk build excluded BLE feature that requires D-Bus, which is
unavailable on OpenWrt targets
- IPv6 routing policy rule added at TUN setup to protect `fd00::/8`
from interception by Tailscale's table 52 default route
- Bloom filter routing no longer swallows traffic when no bloom
candidate is strictly closer than the current node. `find_next_hop`
now falls through to greedy tree routing in that case instead of
returning `NoRoute`, which previously caused dropped packets in
topologies where the tree parent was closer but not a bloom
candidate
- Auto-connect peers now reconnect after a graceful `Disconnect`
notification from the remote side. `handle_disconnect` previously
removed the peer without scheduling a reconnect, orphaning the
@@ -649,19 +669,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
bind with `EAFNOSUPPORT`
([#61](https://github.com/jmcorgan/fips/issues/61),
reported by [@SwapMarket](https://github.com/SwapMarket))
- `fipstop` now uses `ratatui::try_init()` instead of `ratatui::init()`,
so terminal initialization failures (e.g. Docker on macOS Sequoia,
or environments without a usable tty) produce a clean error message
instead of a hard crash
- Tighten TreeAnnounce ancestry validation to match the spanning
tree specification. The receive path now verifies that the
ancestry is structurally consistent with the signed parent
declaration before mutating tree state.
- Spanning-tree updates that change only the internal path between
root and leaf — without changing the root or the depth — now
propagate to leaves correctly. Previously a leaf could continue
routing against a stale internal path until the parent or depth
also changed.
- Make the tree ancestry acceptance unit test deterministic.
`test_tree_announce_validate_semantics_accepts_valid_non_root`
generated a random signing identity while pinning the fixed root
to `node_addr[0] = 0x01`; about 2 in 256 random identities were
numerically smaller than the claimed root, triggering
`AncestryRootNotMinimum`. The test now regenerates the identity
until its `node_addr` is strictly larger than both the fixed
parent and root.
## [0.2.0] - 2026-03-22
+141
View File
@@ -0,0 +1,141 @@
# FIPS v0.2.1
**Released**: 2026-05-11
v0.2.1 is a maintenance release on the v0.2.x line. No new features
and no wire-format changes; operators running v0.2.0 can upgrade in
place. The release rolls up bug fixes and operational hardening for
issues surfaced in v0.2.0 deployments, plus a bloom-filter fill-ratio
validation that protects mesh-size estimates from saturated-filter
inputs.
## At a glance
- 22 commits since v0.2.0, 5 committers plus 2 issue reporters.
- All changes are backwards-compatible with v0.2.0 on the wire.
- Bloom filter fill-ratio validation hardens the FilterAnnounce
ingress path.
- TreeAnnounce ancestry validation tightened to match the
spanning-tree specification.
- Signed-tarball + `.deb` artifact workflow added for tagged
releases; AUR auto-publish on stable tags.
## Behavior changes worth flagging
- **Bloom filter fill-ratio validation** runs on every inbound
`FilterAnnounce`. Filters whose derived false-positive rate exceeds
`node.bloom.max_inbound_fpr` (new config field, default `0.05`) are
rejected silently on the wire, logged at WARN, and counted in a
new `bloom.fill_exceeded` counter. A rate-limited WARN also fires
when the local outgoing filter exceeds the cap.
`BloomFilter::estimated_count` now takes `max_fpr` and returns
`Option<f64>`, returning `None` for saturated filters; this
propagates through `compute_mesh_size` into `estimated_mesh_size`.
- **TreeAnnounce ancestry validation** is now run before tree-state
mutation, enforcing ancestry-self-match, root-single-entry,
parent-second-entry, and root-is-minimum-NodeAddr. Non-conforming
announces are rejected with a WARN. Mixed v0.2.0 / v0.2.1 meshes
may produce WARN log lines on the v0.2.1 side until all peers
upgrade; behavior is correct, log noise only.
## Notable bug fixes
- **Control socket path detection** in `fipsctl` and `fipstop` now
checks for the `/run/fips/` directory instead of the socket file
inside it. Users not yet in the `fips` group get a clear
"Permission denied" error instead of a misleading "No such file"
fallback to `$XDG_RUNTIME_DIR`
([#30](https://github.com/jmcorgan/fips/issues/30), reported by
[@Sebastix](https://github.com/Sebastix)).
- **`fd00::/8` routing protected from Tailscale interception.** The
daemon installs an IPv6 routing-policy rule
(`ip -6 rule to fd00::/8 lookup main priority 5265`) at TUN setup,
so Tailscale's table 52 default route can no longer divert mesh
traffic.
- **Bloom filter routing greedy-tree fallback.** `find_next_hop` no
longer returns `NoRoute` when the bloom candidate set is non-empty
but no candidate is strictly closer than the current node; it
falls through to greedy tree routing instead. Previously, this
caused dropped packets in topologies where the tree parent was
closer but not a bloom candidate.
- **Auto-connect peers reconnect after a graceful Disconnect.**
Previously, a clean upstream shutdown left the auto-connect peer
orphaned; only the link-dead, decrypt-fail, and peer-restart paths
scheduled a reconnect
([#60](https://github.com/jmcorgan/fips/issues/60), reported by
[@SwapMarket](https://github.com/SwapMarket)).
- **`fipsctl connect` rejects FIPS mesh addresses** (`fd00::/8`) for
`udp`, `tcp`, and `ethernet` transports with a clear error message
instead of echoing success while the daemon silently failed the
bind with `EAFNOSUPPORT`
([#61](https://github.com/jmcorgan/fips/issues/61), reported by
[@SwapMarket](https://github.com/SwapMarket)).
- **OpenWrt ipk** cross-compiles cleanly again after excluding the
BLE feature that requires D-Bus, which is unavailable on OpenWrt
targets.
## Packaging
- **Linux release artifact workflow** builds x86_64 and aarch64
tarballs and `.deb` packages on `v*` tag push, with SHA-256
checksums, and publishes them to the GitHub release page.
- **AUR publish workflow** auto-publishes the `fips` PKGBUILD on
stable `v*` tags.
## Upgrade notes
Operator-actionable items when moving from v0.2.0 to v0.2.1:
- **Bloom filter fill-ratio cap (default 0.05).** Inbound
`FilterAnnounce` messages whose derived FPR exceeds the cap are
rejected silently on the wire. Operators with unusually saturated
filters in the field may want to confirm that the default applies
cleanly to their deployment; check the new `bloom.fill_exceeded`
counter if mesh-size estimates drift after upgrade.
- **TreeAnnounce ancestry tightening.** Mixed v0.2.0 / v0.2.1 meshes
may produce WARN log lines on the v0.2.1 side until all peers
upgrade. Behavior is correct, log noise only.
## Getting v0.2.1
- **Linux x86_64 / aarch64**: `.deb` and tarball at the
[v0.2.1 release page](https://github.com/jmcorgan/fips/releases/tag/v0.2.1).
- **Arch Linux**: `fips` from the AUR.
- **OpenWrt**: `.ipk` at the v0.2.1 release page.
- **From source**: `cargo build --release` from a checkout of the
v0.2.1 tag.
The full per-commit changelog lives in
[`CHANGELOG.md`](../../CHANGELOG.md). Issues and discussion at
[github.com/jmcorgan/fips](https://github.com/jmcorgan/fips).
## Contributors
Thanks to everyone who contributed code or bug reports to this
release.
**Code and packaging**:
- [@jcorgan](https://github.com/jmcorgan): release shepherd, bloom
fill-ratio validation, auto-connect reconnect fix, `fipsctl`
mesh-address rejection, control-socket path detection,
Tailscale-vs-`fd00::/8` routing policy, bloom routing greedy
fallback, rustfmt baseline.
- [@Origami74](https://github.com/Origami74): OpenWrt ipk
BLE-feature build fix.
- [@jodobear](https://github.com/jodobear): Linux release-artifact
workflow and target-aware build scripts.
- [@dskvr](https://github.com/dskvr): AUR publish workflow.
- [@SatsAndSports](https://github.com/SatsAndSports): TreeAnnounce
semantic validation.
**Issue reports that drove fixes in this release**:
- [@Sebastix](https://github.com/Sebastix): `fipsctl` / `fipstop`
control-socket path detection
([#30](https://github.com/jmcorgan/fips/issues/30)).
- [@SwapMarket](https://github.com/SwapMarket): auto-connect
reconnect after graceful disconnect
([#60](https://github.com/jmcorgan/fips/issues/60)) and
`fipsctl` mesh-address rejection
([#61](https://github.com/jmcorgan/fips/issues/61)).