pub struct PeerDiagnosticsResult {
pub nickname: String,
pub principal: String,
pub user_id: Option<String>,
pub paired_at: Option<String>,
pub last_addr: Option<String>,
pub hint_addrs: Vec<String>,
pub hint_usable: bool,
pub reachability: Option<PeerReachability>,
pub known_addrs: Option<Vec<KnownAddr>>,
pub hint_addrs_unknown_to_iroh: Vec<String>,
pub iroh_addrs_not_in_hint: Vec<String>,
}Expand description
Result of Request::PeerDiagnostics (#140): the DURABLE per-peer state this node carries,
for diagnosing why a specific long-lived pairing behaves differently from a fresh one.
This surface carries a PEER’s transport coordinates on purpose. The rendered porcelain is
address-free everywhere — nicknames and path KINDS — because that discipline keeps a peer’s
coordinates out of screenshots. (SelfNetwork.direct_addrs already returns this node’s OWN
addresses on status; what is new here is another endpoint’s.) The question this answers is
“what address is this node about to dial, and where did it come from”, which has no answer
without the address. It is your own store’s record of your own paired peers. Do not render it
in ordinary porcelain, and read it before pasting it anywhere public.
The intended use is a paired capture: run it on BOTH ends of a stuck pairing and compare the stored hint against the live path each side reports.
Fields§
§nickname: StringThe peer’s nickname as this node stores it.
principal: StringThe peer’s stable eid: device principal.
user_id: Option<String>The peer’s b64u: user_id if it proved a device→user binding at pairing.
paired_at: Option<String>When the pairing was written (epoch seconds as a string), if recorded. A LONG-LIVED pairing is exactly what #140 is about, so the age is part of the evidence.
last_addr: Option<String>The persisted dial HINT, verbatim as stored — the durable state a freshly paired identity
does not have. None for a peer added without one.
It is MERGED with discovery rather than replacing it — iroh inserts it as one more
candidate path (Source::App) and then triggers address lookup.
But that lookup is skipped when a path is already selected. iroh’s
trigger_address_lookup returns early if selected_path.is_some(), and a selected path is
cleared only when the last connection to that peer closes. So on a pair that already holds
an open RELAYED connection — live sessions, dial-backs, a working relay — discovery does
NOT re-run, and this hint is the only addressing the dial contributes. Do not read “merged,
so a stale hint is harmless” as unconditional; it is least true in exactly the state a
stuck pairing is in.
It is the only durable per-peer state ON THIS NODE’S DISK that the dial path reads, which
is what makes it the first thing to compare between two ends. It is not the only durable
state a long-lived identity carries — a published discovery record under the same key, and
SelfNetwork::identity_conflict_epoch, live elsewhere.
hint_addrs: Vec<String>The addresses parsed out of last_addr, for reading without a JSON round trip: IP
addresses verbatim, relay URLs as relay <url> and SANITIZED to scheme+host+port (an
operator’s relay URL can carry a userinfo token, and this output is meant to be pasted into
an issue). Empty when the hint is absent, unparseable, or for a different endpoint — all of
which degrade to an id-only dial.
A relay … entry with no IP alongside it is worth noticing: that hint can never punch.
hint_usable: boolWhether the stored hint actually contributes anything to a dial. false with a present
last_addr means it is being silently discarded — because it does not parse, because its
embedded id is a different peer, or (since 0.53.0, #203) because every address in it is one
that can never be a QUIC peer and was filtered out. last_addr is reported verbatim above,
so comparing it against hint_addrs distinguishes the three.
reachability: Option<PeerReachability>This node’s LIVE view of the peer, read straight from the reachability cache — the same
values status reports, repeated here so one capture holds both the durable and the live
side. None when this peer has never been probed, which is the honest answer on a
freshly restarted daemon; it is not the same as unreachable.
Read from the cache rather than through status’s projection deliberately: that projection
spawns a background probe for every stale peer, which would make this diagnostic a
participant in the reproduction it is meant to observe.
known_addrs: Option<Vec<KnownAddr>>What iroh currently holds for this endpoint, as opposed to what this node stored
(#140, api_minor >= 56). The other half of the capture, and the half the standing
hypothesis lives in.
Everything above describes our own disk. Until this field there was no way to see what iroh
made of it — whether the hint’s addresses are in its remote map at all, whether discovery
contributed anything alongside them, or which address is carrying traffic. Read straight off
Endpoint::remote_info, a point read of the remote map: no dial, no probe, no address
lookup, so this stays safe to run ON a live reproduction.
None means iroh currently holds NO ENTRY — which is not the same as an empty list, and
is not the same as “never heard of”. iroh reaps a remote’s state about 60 seconds after
the last connection to it closes (ACTOR_MAX_IDLE_TIMEOUT), so None is the normal answer
both for a peer never dialled AND for one talked to a few minutes ago. An empty list means
iroh has an entry and holds no address in it.
This distinction matters most to the question #140 is asking, which is about durability over
time: reading None as “iroh never knew this peer” would be wrong on any daemon that has
been idle, which is most of them.
hint_addrs_unknown_to_iroh: Vec<String>Addresses in hint_addrs that iroh does not currently hold.
The hint is written whole: set_last_addr REPLACES the stored value with one built from the
live connection’s open IP paths, so these are not accumulated cruft — they are addresses
that were real at the last successful connection and are absent from iroh’s view now. (An
earlier version of this doc said #124 “amends but never removes”; that is false, and
dial_hint.rs says so in as many words.)
Empty whenever known_addrs is None, because there is no view to
difference against. Reporting the whole hint as unknown when iroh simply has no entry would
call a current, correct hint stale on every idle daemon.
INFERRED by set difference, not read: iroh 1.0.3’s TransportAddrInfo carries no
provenance, so “did this come from our hint or from discovery?” cannot be answered directly.
Naming it an inference is the honest version.
iroh_addrs_not_in_hint: Vec<String>The converse: what iroh holds that our stored hint does not name — inferred the same way,
and empty for the same reason when known_addrs is None.
Trait Implementations§
Source§impl Clone for PeerDiagnosticsResult
impl Clone for PeerDiagnosticsResult
Source§fn clone(&self) -> PeerDiagnosticsResult
fn clone(&self) -> PeerDiagnosticsResult
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more