Files
fips/docs/releases/release-notes-v0.5.1.md
Johnathan Corgan 30a283907b Correct the release documents the readiness pass found wrong
The closing code fence in the release notes carried an info string, so under
CommonMark it was not a closer and the block ran to the end of the file.
Rendered through GitHub's own markdown endpoint the published body stopped
emitting headings after "Upgrade notes" and ended inside a code block, so
the download section, the changelog and security links and the contributor
credits all became preformatted text. Rendering the corrected file the same
way returns all seven headings and a working release link. A lint fix had
rewritten both fences rather than only the opening one.

The post-upgrade instruction named a subcommand that does not exist: it said
to run fipsctl status, which exits 2, where the command is fipsctl show
status. That instruction is aimed at exactly the users this release exists
for.

Several claims were broader than the tree supports, and each is narrowed to
what is true. The two discovery fixes carry no platform gating, so the notes
no longer tell macOS, Windows, FreeBSD and OpenWrt users that nothing
changed for them, and no longer say the release contains no behavioral
change. The supported-distribution paragraph now covers the .deb and the
tarball rather than all Linux artifacts, since Arch and NixOS build from
source and OpenWrt is a musl target that does not depend on the glibc floor.
The install-test sentence says x86_64, because the arm64 package is
install-tested by nothing. The changelog no longer claims every producer
runs the floor check, since the deprecated host-build targets do not.

The counter guidance was wrong about which counter moves, the objdump line
prints the symbol beside the version, and the two relative links in the
notes resolve inside the repository but 404 in a release body, so they are
absolute now. That last change also makes the root mirror byte-identical to
the versioned copy for the first time.

The FreeBSD install line takes a concrete filename again: a placeholder is
not pasteable, which is the whole point of an install instruction.
2026-09-06 20:20:56 +00:00

9.0 KiB

FIPS v0.5.1

Released: 2026-09-06

v0.5.1 is a maintenance release on the v0.5.x line and it exists for one reason: every Linux artifact from v0.3.0 through v0.5.0 installs cleanly on Debian 12 and Ubuntu 22.04 and then cannot start. The package installs, the package manager reports success, and the daemon fails to load with GLIBC_2.39 not found. If you run FIPS on either of those distributions from a published package, you have never had a working daemon, and this release is the fix.

It carries two discovery fixes as well, one of them an external contribution. There is no wire format change and no new configuration.

At a glance

Who should upgrade

  • Debian 12 and Ubuntu 22.04, from a package: upgrade. The daemon on those systems has never run. Nothing you can configure works around it.
  • Any other Linux: upgrade at your convenience. Your daemon was running, and you gain the two discovery fixes.
  • macOS, Windows, FreeBSD and OpenWrt: the packaging defect never affected you, since it was in how the Linux artifacts were built. You do get the two discovery fixes, which are not gated by platform.
  • From source: you were never affected. A binary you built runs against the C library you built it on.

Before you upgrade

Nothing to do. No configuration key was added, removed or given a new meaning, and no configuration that loaded under v0.5.0 fails to load here.

What changed

  • Linux packages and the systemd tarball now install and run on Debian 12 and Ubuntu 22.04.
  • A node no longer relays away the answer to its own lookup.
  • A returning copy of a node's own lookup request is no longer counted against the peer that delivered it.

The Linux packaging defect

What went wrong. Every Linux artifact from v0.3.0 onward was built on the newest available runner. That runner's C library turns the standard library's pidfd references into a hard GLIBC_2.39 version requirement, instead of the weak, runtime-checked references they are meant to compile to. The loader refuses an image on that entry alone, so fips, fipstop and fips-gateway could not start on any system with an older C library. fipsctl was unaffected, which is why an install checked by running a command looked healthy while the daemon was dead.

No source code caused the packaging defect and none was changed to fix it. The defect was in the build environment. The two discovery fixes below are this release's only behavioral change, and they are unrelated to it.

Why the declared dependency did not stop it. The .deb said it needed libc6 with no version, which every glibc satisfies. So a package whose binaries required 2.39 installed happily on a system with 2.35.

What is different now. The Linux artifacts are built in a container pinned to the oldest supported distribution, declared in packaging/build-floor.env. Every producer runs testing/check-glibc-floor.sh on what it made, so an artifact that would not load fails the build rather than reaching you. The declared dependency is derived from the binaries themselves rather than written by hand, so it states the floor it was actually built against and re-derives per architecture. One script now produces the Linux artifacts, and the systemd tarball takes its binaries out of the package rather than from a second, unchecked set.

What was measured. The .deb and the tarball were built and floor checked on x86_64 and aarch64; both architectures carry a 2.34 floor, which clears the 2.35 the packaging declares. Five distributions install the package and start the daemon in CI. Separately, a project that consumes FIPS inside an initramfs reports five machines of five unlocking an encrypted root over the mesh, including Debian 12 and Ubuntu 22.04, both of which fell back to a console prompt on v0.5.0.

What was not measured. No published v0.5.1 artifact existed when this was written; the checks above ran against artifacts built by the same scripts the release workflow uses. Reproducibility was measured same-machine at v0.4.2 and cross-machine reproducibility has never been tested.

Discovery fixes

A node relayed away the answer to its own lookup

A lookup request is flooded to every tree peer whose bloom filter claims the target, so a false positive can send a copy into the wider network and circulate it back to the node that started it. The only identity test on arrival asked whether the request named this node as the target, which a lookup this node originated never satisfies. The returning copy was therefore filed as ordinary transit under the node's own request id. When the target answered, the reply was reverse-path forwarded to the peer that had looped the request back, the pending lookup was never satisfied, and discovery reported that its requests went unanswered while the answers were in fact arriving.

An inbound response is now matched against this node's outstanding lookups before the transit dedup record, and a returning copy of the node's own request is dropped rather than recorded, so that id never enters the transit cache.

This was a race rather than a hard failure: a reply that beat the looped copy found a clean cache and succeeded. It grew likelier as the bloom fill ratio rose, which means it got worse as a mesh grew. Contributed by Arjen.

req_duplicate counted something it does not mean

The fix above drops the returning copy, and it first recorded that drop under the existing req_duplicate rejection, whose documented meaning is that a peer resent a request. A returning copy has a nonzero floor in healthy operation and rises with the bloom fill ratio, so folding the two together put a permanent number on a counter an operator reads as neighbour misbehaviour, and made the two events indistinguishable.

It now has its own rejection reason and counter, req_own_loopback, shown in fipstop as "Own Loopback". req_duplicate returns to meaning only what it says.

If you watch these counters, expect a new non-zero Own Loopback to appear on a node that originates lookups. That is traffic that was previously counted elsewhere or not at all, correctly attributed, rather than a new fault.

Compatibility

v0.5.1 is wire-compatible with v0.5.0. No frame gains, loses or resizes a field, so a mixed mesh works and nodes can be upgraded one at a time with no coordinated restart. The two discovery changes alter what a node does with a message it already parsed; neither changes what is on the wire.

The library surface is unchanged. Configuration is unchanged.

Upgrade notes

Package upgrade, Debian and Ubuntu. The usual upgrade replaces the binaries and restarts the service. On Debian 12 and Ubuntu 22.04 the daemon will start for the first time, so this is a first start rather than a restart: check fipsctl show status afterwards and expect to see peer establishment, not a resumed session.

Check what you actually have. If you want to confirm the floor of an installed binary rather than trust the version string:

objdump -T /usr/bin/fips | grep GLIBC_ | sed 's/.*GLIBC_//' | sort -uV | tail -1

An artifact from this release prints 2.34) __libc_start_main. One from v0.5.0 or earlier prints 2.39) pidfd_spawnp, which names the symbol that caused this.

Rolling upgrade. No coordination is needed. Upgrade nodes in any order.

Getting v0.5.1

  • Linux x86_64 / aarch64: .deb and tarball at the v0.5.1 release page.
  • Arch Linux: fips from the AUR.
  • macOS: .pkg at the v0.5.1 release page.
  • Windows: ZIP at the v0.5.1 release page.
  • FreeBSD (x86_64): .pkg at the v0.5.1 release page.
  • OpenWrt: .ipk (OpenWrt 24.x and earlier) or .apk (OpenWrt 25+) at the v0.5.1 release page.
  • From source: cargo build --release from a checkout of the v0.5.1 tag (Rust 1.94.1 per rust-toolchain.toml; libclang-dev is a required Linux build prerequisite).
  • Nix / NixOS: nix build .#fips from a checkout of the v0.5.1 tag builds the binaries from source with the pinned toolchain and no manual prerequisites (see the Nix section of packaging/README.md).

There is no Android daemon artifact. Android is supported as an embedded crate.

The full per-commit changelog lives in CHANGELOG.md. Issues and discussion at github.com/jmcorgan/fips. Security reports have a private channel; see SECURITY.md.

Contributors

Thanks to everyone who contributed code, packaging work, bug reports, or reviews to this release.

  • @jmcorgan (Johnathan Corgan): release shepherd; the Linux build and floor-checking work, the loopback rejection counter, and the install-suite fix that made the defect visible instead of hanging.
  • @Origami74 (Arjen): the lookup originator fix, so a node accepts the answer to its own lookup instead of relaying it away (#141).