Repository navigation
NexaPipe v0.3.0
0.3.0 answers the question its own logs could not.
A running instance has always been able to say what happened to a request, and
nothing about itself: how many peers are connected, over which path, how many
backends are actually in rotation, how much work is still in flight. There is a
second listener for that now — off until you bind one, and reachable only from
the machine it runs on.
Health, metrics and live state behind loopback
[admin]
listen_addr = "127.0.0.1:9090" # absent => the listener is not bound at all
[metrics]
enabled = true # serves /metrics on it; default: false| Path | Auth | Answers |
|---|---|---|
GET /healthz |
none | 200 ok for as long as the process is serving. |
GET /metrics |
none | Instance metrics in Prometheus text format. |
GET /v1/status, /v1/routes, /v1/clients, /v1/connections, /v1/health |
token | The same instance's live state, as JSON, read fresh on every request. |
curl -s http://127.0.0.1:9090/healthz
curl -s http://127.0.0.1:9090/metrics
curl -s -H "authorization: Bearer $(cat config.toml.admin-token)" \
http://127.0.0.1:9090/v1/status
nexapipe status # the five endpoints, grouped
nexapipe status --json # the same read, as one documentWhat /metrics exposes:
| Metric | Meaning |
|---|---|
nexapipe_connections_total / _active |
Connections accepted, and how many are open. |
nexapipe_connections_by_path{kind="direct"|"relayed"} |
Read from iroh rather than guessed from an address, so "no relaying" is a number, not a claim. |
nexapipe_requests_total{status_class}, nexapipe_request_duration_ms_total |
Requests by 2xx/4xx/5xx, and the milliseconds they took. Divide one by the other for a mean that survives restart-free scraping. |
nexapipe_l4_flows_total{proto,status_class} |
Raw TCP and UDP flows that ended, and how. |
nexapipe_backends_up / _down |
Backends in and out of rotation. |
nexapipe_in_flight, nexapipe_uptime_seconds, nexapipe_build_info{version} |
Work still in flight, uptime, and which build you are scraping. |
Two deliberate edges: with the listener up but metrics off, /metrics is 404
rather than an empty body, so a scraper can tell "disabled" from "no traffic
yet"; and a missing or wrong token is 503 rather than an open door. The token
is generated by the server on first start and written next to the config as
<config>.admin-token — there is deliberately no key for it inside the config,
because a credential in the file an operator edits, copies and commits is what
this design avoids.
Loopback only, and there is no expose. [server] expose exists because the
plaintext listener may sit behind something else that gates the port; nothing on
this one should reach the network, since it names your routes, clients and
backends. A non-loopback bind is refused at startup. To scrape from another host,
tunnel it: ssh -L 9090:127.0.0.1:9090 <host>.
Android asks before it hands out credentials
The TOTP secret of an endpoint, its otpauth export, and any change to the relay
configuration now require confirmation first — a biometric where there is one,
the screen-lock credential otherwise — and stay open for two minutes after one
confirmation. SecretStore already sealed these at rest; what was missing was
anything asking before handing them back. A device enrolled with neither a
biometric nor a screen lock is refused rather than downgraded. Proxy start and
stop are deliberately not gated: the VPN has to come back after a reboot with
nobody present.
Every request has an ID
32 hex digits per request — one per entry point, not one per connection —
appended to that request's access line as id=<hex>, carried by a request
tracing span alongside its method, URI and status, and returned to the caller as
x-request-id. A backend that sends its own is dropped rather than duplicated,
so there is one ID to quote and it is ours. Appended rather than inserted, too:
every field before it keeps the column it had.
Full changelog
Added
- The auxiliary loopback listener described above:
[admin] listen_addr, with
GET /healthzalways andGET /metricsbehind[metrics] enabled. - Instance metrics: connections (total, active, and by direct/relayed path),
requests by status class and duration, L4 flows by protocol and status,
backends up and down, work in flight, and uptime. Written by hand, so no
prometheuscrate was added andCargo.lockis untouched. - The read-only management surface behind a generated token:
GET /v1/status,
/v1/routes,/v1/clients,/v1/connectionsand/v1/health. nexapipe status— a client of those same endpoints, with--json. It reads
the running instance rather than reopening the config, so the instance stays
the one thing that decides what its state is.- The Android confirmation gate over endpoint secrets,
otpauthexport and relay
configuration changes. - A request ID per entry point: on the access line, on the tracing span, and as
x-request-id. docs/iroh-boundaries.md— what still depends on third-party infrastructure
whateverrelay_modesays.customconstrains only this endpoint, and
Endpoint ID discovery still queriesdns.iroh.linkin every mode.- A
CHANGELOG.md. 0.2.0 shipped without one, so its entry there is
retrospective; 0.3.0 is the first release recorded in it as it happened.
Changed
- Hot-reload rules, previously scattered across the configuration sections, are
now one table in both READMEs: what a reload applies, and what needs a restart. - The access log line ends with
id=<hex>. - HTTP responses carry
x-request-id. An L4 flow and a TLS passthrough are
tunnels rather than requests: they get an ID on the access line and the span,
and have no HTTP response to put a header on.
Fixed
- Both READMEs quoted the iroh version wrongly in two ways at once: as pinned at
1.0.1, when it is declared^1.0.1andCargo.lockresolves 1.2.0. The line
now defers todocs/iroh-boundaries.md.
Upgrading
Nothing is removed and no config key changes meaning — every config that
starts under 0.2.0 still starts under 0.3.0. Three things to know:
- Nothing new is exposed by default. Without an
[admin] listen_addrthe
listener is not bound, so upgrading changes no attack surface. [metrics] enabledwithout an[admin]section logs a warning and serves
nothing.[admin] listen_addr,[metrics] enabledand[server] listen_addrare
restart-only: moving a listener is a restart. Everything else reloads, as
the new table in the README spells out.
Downloads
Everything is attached below, one tag shipping all three products: the server
archives for eight targets, the desktop bundles for six platforms, and the signed
arm64-v8a APK. Each desktop bundle carries a .sig; macOS updater payloads and
a merged latest.json are what the in-app updater reads; SHA256SUMS.txt covers
every file in the release.
curl -sLO https://github.com/open-nexa/nexapipe/releases/download/v0.3.0/SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txtFull list with checksums and per-platform buttons:
https://open-nexa.github.io/nexapipe/v0.3.0.html · complete history in
CHANGELOG.md ·
what comes next in
docs/ROADMAP.md.