Identity finally goes as far as the device. One credential per device, issued at enrollment and revocable on its own, and every install answering as one named device of itself — so revoking a laptop no longer means re-enrolling a phone, and a device struck out loses the connections it is already holding instead of keeping them until it hangs up. Alongside it: request latency as a histogram, the write half of the management surface, and a desktop that shares its DNS with the other TUN apps on the machine.
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.
Published with everything else, as one multi-arch manifest for
linux/amd64 and linux/arm64. Both READMEs carry a
systemd unit for the server now as well.
# This release exactly docker pull ghcr.io/open-nexa/nexapipe:0.6.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.6.0/nexapipe-0.6.0-x86_64-unknown-linux-musl.tar.gz
tar -xzf nexapipe-0.6.0-x86_64-unknown-linux-musl.tar.gz
cd nexapipe-0.6.0-x86_64-unknown-linux-musl && ./nexapipe --version
# macOS, Apple silicon
curl -LO https://github.com/open-nexa/nexapipe/releases/download/v0.6.0/nexapipe-0.6.0-aarch64-apple-darwin.tar.gz
tar -xzf nexapipe-0.6.0-aarch64-apple-darwin.tar.gz
cd nexapipe-0.6.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.6.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.6.0/SHA256SUMS.txt grep nexapipe-0.6.0-aarch64-apple-darwin.tar.gz SHA256SUMS.txt | shasum -a 256 -c -
All assets: SHA256SUMS.txt.
0.6.0 follows 0.5.0, released 7 October 2026. Two configuration keys are added
and none is removed, so a config.toml that works on 0.5.0 loads unchanged — but
four things behave differently without being configured to, and all of them are listed under
before upgrading.
[auth.clients.<id>] gains a devices table: one secret per
device, issued at enrollment and revoked on its own, so striking one out no longer rotates the
rest. A client carrying only secret keeps working exactly as it did.6, which both clients report as “the
credential is gone” rather than as a code typed wrong.client=… device=…, and /v1/connections
answers who is connected rather than only how many.nexapipe client list|add|revoke, each with --device, writes
config.toml through the same lock the server reads it with, and a running server
picks the change up on its next reload. /v1/* stays read-only on purpose.unnamed_device_allowed (default true): set to false, a
peer naming no device is refused rather than answered under the client's shared secret./metrics reports request latency as a histogram —
_bucket{le="…"} beside _sum and _count — replacing
nexapipe_request_duration_ms_total, which no longer exists. A p99 no longer takes
arithmetic.systemd unit for the server, and
docs/self-hosted-relay.md one for the relay. The workspace declares the Rust
version it needs: 1.89.One line each; the full entry is in CHANGELOG.md.
[auth.clients.<id>].devices./v1/connections name the client and the device.nexapipe client list|add|revoke, each with --device.unnamed_device_allowed, default true.systemd unit for the server in both READMEs and one for the relay; a
size and performance roadmap./metrics is a histogram, not the counter
nexapipe_request_duration_ms_total.1.89.6.--force replaces one on purpose.config.toml and the --qr-out file were created readable by everyone
and followed a symlink to reach the file; both are created private now, and the QR file will
not overwrite one that is there.nexapipe client add reported a secret it had not written, and exited as though
it had.[peers] refuses a key it does not recognise: allowd was dropped and
left a server that looked restricted and was not.smoltcp patch; a tunnel whose DNS was never hijacked no longer runs as though it
had been; plus the Linux TUN resolver, a service an upgrade left behind, a stopped macOS
service, the service-version permission and the Reduce Motion indicator.config.toml
that works on 0.5.0 loads unchanged on 0.6.0, and a client written before devices existed
answers exactly as it did.--force now.unnamed_device_allowed defaults to true, so a peer naming no device
is still answered under the client's shared secret until a client says otherwise./metrics loses nexapipe_request_duration_ms_total and gains the
histogram. A scrape looking for the old counter will not find it: read
nexapipe_request_duration_ms_sum instead, and divide by
nexapipe_requests_total for the same mean.6 into “the credential is gone”; an
older client library reads it as an unknown code.:0.6.0 rather than :latest if you want the version to
stay put.