Commit Graph
2 Commits
Author SHA1 Message Date
tsunagiandClaude Opus 5 776eedc669 Report members, not symptoms
A peer going away showed up as three unrelated yellow rows: no peers
authenticated, N dial failures, one packet to an address nobody owns.
Each was true and none of them said the actual thing, which is that a
member we know about is offline. Worse, they are cumulative, so after
the peer came back the report still looked broken.

Two changes behind that.

Members are now a list, joined from the three sources that each know
part of the answer: signed state says who belongs and keeps saying it
while they are away, the session list says who is here, the overlay says
whose tunnel is up. Online first, then away, this agent left out because
the device section already covers it. An absent member is stated rather
than flagged — in a mesh of laptops being away is the ordinary condition
— and its failed dials are attributed to it instead of floating free as
a network-wide number.

Counters are history and no longer grade anything. Grading them is what
kept the report red long after the cause had gone. The one exception is
context-sensitive rather than cumulative: handshake failures with nobody
connected is the signature of a mismatched secret, so that is called
out.

NetworkStatus grows a member roster from the signed records, and the
overlay report carries the endpoint id so a tunnel can be matched to its
session. Note what the roster cannot do: a member is in signed state
only once it has claimed something, which today means an IPv4 address,
so an IPv6-only network still has no durable roster. Hostnames are not
persisted either, so an absent member is named by its id.

The control socket gained a version word. Adding these fields changed
how postcard parses the bytes, and without it a client one build ahead
of its agent reported "Found an Option discriminant that wasn't 0 or 1".
The check names a mismatch for whichever side is newer; an older agent
reading a newer request just drops the connection, so the CLI offers
that as a possibility rather than asserting it. It also now separates an
agent that is absent, which is an ordinary answer, from one that is
there and will not answer, which is a fault.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 15:26:16 +01:00
tsunagiandClaude Opus 5 be459e5bd0 Add tsunagi status over a local control socket
There was no way to ask a running agent what it was doing; the only status
came from the periodic print of the `up` process itself.

The new ipc module is an adapter over the public API: nothing in the agent
core knows it exists, so a Windows named pipe or an authenticated loopback
socket can be added beside it. It is also a different interface from the
peer-to-peer protocol — between processes on one machine, authorised by
filesystem permissions rather than the network secret. The socket is 0600
inside an owner-only directory, the wire format is length-prefixed postcard
with the same bounds the network protocol uses, and the report types are
their own stable format rather than the crate's internals.

The socket path is derived from the state directory into XDG_RUNTIME_DIR
when there is one. A Unix socket address is limited to about 100 bytes, and
a deeply nested state directory overflows it — which is exactly what
happened on the first attempt.

Two presentation fixes while here. Multicast is counted separately from
unroutable traffic, because Linux emits multicast on every IPv6 interface
and it was showing up as "packets for unknown addresses" on a healthy
agent. And WireGuard protocol errors are no longer added into the dropped
counter: a few are normal while both ends start a handshake at once, and a
working tunnel was reporting "dropped 3" with no traffic at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 12:53:04 +01:00