Nothing here opens a new front. Health finally decides which node the next
request goes to, the backends that cannot answer GET /health are asked anyway, and
the server and both clients count the bytes they carry. Alongside it: an image in the release,
your own relay documented, and a desktop whose DNS no longer outlives the tunnel.
Pick a build below if the guess is wrong.
Client app first, then the server. Nothing needs an account, and nothing is gated.
One APK per ABI: arm64-v8a for every phone, x86_64 for the
emulator images a development machine can run. Desktop bundles are signed with the Tauri updater
key; the in-app updater reads latest.json in the same release. iOS is not a supported
platform — see the
roadmap.
Checksums for every asset above: SHA256SUMS.txt.
Each archive holds the nexapipe binary, the client cdylib
for embedding, config.toml.2fa.example, the README and the licence. Checksums for
every asset above:
SHA256SUMS.txt.
New in this release: the image is published with everything else, as one multi-arch
manifest for linux/amd64 and linux/arm64.
# This release exactly docker pull ghcr.io/open-nexa/nexapipe:0.5.0 # The newest stable release (:latest never points at a pre-release) docker pull ghcr.io/open-nexa/nexapipe:latest
docker-compose.yaml names the published image, so docker compose up
pulls it rather than building — --build still builds locally under the same name.
No package manager yet — these are the release assets, straight from the tag.
# Linux x86_64, static binary
curl -LO https://github.com/open-nexa/nexapipe/releases/download/v0.5.0/nexapipe-0.5.0-x86_64-unknown-linux-musl.tar.gz
tar -xzf nexapipe-0.5.0-x86_64-unknown-linux-musl.tar.gz
cd nexapipe-0.5.0-x86_64-unknown-linux-musl && ./nexapipe --version
# macOS, Apple silicon
curl -LO https://github.com/open-nexa/nexapipe/releases/download/v0.5.0/nexapipe-0.5.0-aarch64-apple-darwin.tar.gz
tar -xzf nexapipe-0.5.0-aarch64-apple-darwin.tar.gz
cd nexapipe-0.5.0-aarch64-apple-darwin && ./nexapipe --version
# Linux — verify before extracting: the check looks for the archive beside # SHA256SUMS.txt, so it has to run before the cd into the directory curl -LO https://github.com/open-nexa/nexapipe/releases/download/v0.5.0/SHA256SUMS.txt sha256sum -c --ignore-missing SHA256SUMS.txt
# macOS — the same check with shasum, which is what ships there. macOS 26 # added sha256sum, but every release older than that has only shasum, and not # every version of it understands --ignore-missing, so grep narrows the list # to the one archive you actually downloaded. Still before the cd. curl -LO https://github.com/open-nexa/nexapipe/releases/download/v0.5.0/SHA256SUMS.txt grep nexapipe-0.5.0-aarch64-apple-darwin.tar.gz SHA256SUMS.txt | shasum -a 256 -c -
All assets: SHA256SUMS.txt.
0.5.0 follows 0.4.0, released 30 September 2026. No configuration key changes,
and nothing stopped working that used to — but a passthrough or tcp
route whose backend refuses connections is now reported unhealthy where it previously
looked fine.
Option<usize>, so
"none of them" is a sayable answer — index 0 was always a valid reply to
"which node", which is why the old code could never say no.GET /healthhttp were probed. A TLS listener behind a
passthrough route had no health at all: it surfaced as a connect error on whichever
flow happened to try it first.passthrough and tcp routes are probed by connecting to them —
a TLS backend by completing its handshake. That is deliberately the smaller claim: something is
listening, not that it works.udp-only route is still not probed, now for a stated reason: every probe
available is one every backend would fail, and a pool emptied by a check that could never have
succeeded is worse than an unchecked one.nexapipe_traffic_bytes_total{direction="sent"|"received"}, and
/v1/status carries the same two numbers. They are counted on the client leg
and nowhere else — the one place where one number means one thing across paths this
different. An HTTP response takes its count from what the access log already printed, so the log
line and the Prometheus number cannot disagree.0600 file the other platforms fall back to, and is moved rather than
replaced, so nothing already stored becomes unreadable.launchctl unload,
systemctl stop and the shutdown broadcast all just killed it. Windows was the only
platform doing this correctly; it now has graceful shutdown, an explicit launchd
ExitTimeOut, and a Windows service entry point that matches what the
nexa-service binary already did.…254 host address of a candidate block counts as ours, never the
/24 around it: 10.0.0.0/24 is both a candidate block and the subnet a
great many home LANs use.ghcr.io/open-nexa/nexapipe — one multi-arch manifest,
linux/amd64 and linux/arm64 — under the same tag that already
drives the archives, the bundles and the APK.docs/self-hosted-relay.md takes a relay from nothing to running, and how to point
NexaPipe at it. It does not remove the third-party dependency — only changes whom you
depend on.iroh-relay, but
not followed end to end on a machine that had nothing on it. The Android notification's rates are
verified by unit tests and by compilation only. Everything else here has been compiled, tested and
driven by hand on the platform it touches.
cfg(unix) fixtures.BiometricPrompt on a real Android device. Nothing about the designs changed; what
changed is that 0.4.0's note no longer has to say it might not work.passthrough and tcp routes get a TCP connect probe, a TLS
backend one that completes its handshake.nexapipe_traffic_bytes_total and a traffic object on
/v1/status.docs/self-hosted-relay.md.0600 file rather than the
keychain, and is migrated out of it on first launch.config.toml that works on
0.4.0 loads unchanged on 0.5.0.passthrough and tcp routes are probed now. A backend that refuses
TCP connections will be reported unhealthy and, in a multi-backend route, skipped — where
it previously looked healthy and failed on the request./metrics gains a counter and loses nothing; /v1/status gains a
traffic object.docker-compose.yaml names the published image, so docker compose up
pulls instead of building. Pin :0.5.0 rather than :latest if you want
the version to stay put.