Documentation
¶
Overview ¶
Package doctor is the health check for the relays and the network path.
Why it was the FIRST step of phase 2: BRIEF section 4 says "test the real relay list live in phase 2 and update it", and section 8 carries the warning that "some relays may drop ephemeral events". Before writing the signaling flow we had to measure that the relays really work; otherwise code gets written blind on top of a relay list that does not.
For each relay, in order:
- the NIP-11 information document — does it want auth, what are the limits?
- the WebSocket connection — is it reachable, and how fast?
- the ephemeral echo test — can a kind 25050 event be published and read back FROM A SUBSCRIPTION? This is the critical step: a relay can accept the event with "OK" and drop it silently, and then signaling does not work.
Plus STUN reachability and the NAT type guess (which belongs to BRIEF phase 4, but is needed here to diagnose connection problems).
Index ¶
Constants ¶
const ( // DefaultNip11Timeout bounds the information document. DefaultNip11Timeout = 8 * time.Second // DefaultConnectTimeout bounds the WebSocket connection. DefaultConnectTimeout = 10 * time.Second // DefaultEchoTimeout bounds a published event coming back from the // subscription. DefaultEchoTimeout = 8 * time.Second // DefaultSTUNTimeout bounds a single STUN query. DefaultSTUNTimeout = 5 * time.Second )
The default durations.
const DefaultTURNTimeout = 10 * time.Second
DefaultTURNTimeout bounds a single TURN allocation attempt.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type NATVerdict ¶
type NATVerdict string
NATVerdict is the NAT type guess.
const ( // NATUnknown means there is not enough data (only one server was // reached, or none at all). NATUnknown NATVerdict = "unknown" // NATFriendly means two STUN servers saw the same external port: // endpoint-independent mapping. WebRTC can connect directly. NATFriendly NATVerdict = "friendly (no TURN needed)" // NATSymmetric means the ports differ: symmetric NAT. TURN may be // needed (phase 5). NATSymmetric NATVerdict = "symmetric (TURN may be needed)" // NATNoSTUN means no STUN server could be reached. NATNoSTUN NATVerdict = "STUN unreachable" )
The possible NAT verdicts.
type Nip11Summary ¶
type Nip11Summary struct {
Name string `json:"name,omitempty"`
Software string `json:"software,omitempty"`
SupportedNIPs []int `json:"supported_nips,omitempty"`
AuthRequired bool `json:"auth_required"`
PaymentRequired bool `json:"payment_required"`
RestrictedWrites bool `json:"restricted_writes"`
MaxMessageLength int `json:"max_message_length,omitempty"`
MaxContentLength int `json:"max_content_length,omitempty"`
MinPowDifficulty int `json:"min_pow_difficulty,omitempty"`
}
Nip11Summary holds the useful fields of the information document.
type Options ¶
type Options struct {
Nip11Timeout time.Duration
ConnectTimeout time.Duration
EchoTimeout time.Duration
STUNTimeout time.Duration
TURNTimeout time.Duration
}
Options makes the durations tunable.
Fields left at zero fall back to the defaults. Tests pass short durations to cut the run time; production uses the defaults.
type RelayReport ¶
type RelayReport struct {
URL string `json:"url"`
Verdict Verdict `json:"verdict"`
Nip11 *Nip11Summary `json:"nip11,omitempty"`
Nip11Err string `json:"nip11_err,omitempty"`
Connected bool `json:"connected"`
ConnectMs float64 `json:"connect_ms,omitempty"`
Published bool `json:"published"`
PublishMs float64 `json:"publish_ms,omitempty"`
Echoed bool `json:"echoed"`
EchoMs float64 `json:"echo_ms,omitempty"`
Err string `json:"err,omitempty"`
}
RelayReport is a single relay's result.
func CheckRelays ¶
CheckRelays tests the given relays in parallel.
func (RelayReport) Usable ¶
func (r RelayReport) Usable() bool
Usable says whether the relay can be used for signaling.
type Report ¶
type Report struct {
Relays []RelayReport `json:"relays"`
STUN STUNReport `json:"stun"`
// TURN is only filled in when --turn was given.
TURN *TURNReport `json:"turn,omitempty"`
// UsableRelays are the addresses of the usable relays.
UsableRelays []string `json:"usable_relays"`
// Advice is the one sentence that joins the NAT verdict and the TURN
// result.
Advice string `json:"advice"`
}
Report is doctor's whole result.
func Run ¶
func Run( ctx context.Context, relays []string, iceCfg ice.Config, log *slog.Logger, opts Options, ) Report
Run runs the full check.
With an empty iceCfg.TURN the TURN check is not run at all and the report carries no TURN field.
func (Report) OK ¶
OK says whether the agent is in a state where it can work.
Two conditions: at least one usable relay AND (either no TURN was given or at least one of the given TURNs grants an allocation). Symmetric NAT alone is NOT a failure — it is a fact about the network, so it does not affect the verdict.
type STUNReport ¶
type STUNReport struct {
Servers []STUNServerReport `json:"servers"`
Verdict NATVerdict `json:"verdict"`
Note string `json:"note,omitempty"`
}
STUNReport is the result of the STUN section.
func CheckSTUN ¶
func CheckSTUN(ctx context.Context, urls []string, opts Options) STUNReport
CheckSTUN queries the STUN servers and produces the NAT type guess.
How the guess works: ask two different STUN servers and compare the EXTERNAL PORTS they see. Same port -> endpoint-independent mapping (WebRTC can connect directly). Different ports -> symmetric NAT, TURN may be needed (phase 5). With only one server reachable, no verdict can be made.
type STUNServerReport ¶
type STUNServerReport struct {
URL string `json:"url"`
OK bool `json:"ok"`
MappedIP string `json:"mapped_ip,omitempty"`
Port int `json:"port,omitempty"`
Ms float64 `json:"ms,omitempty"`
Err string `json:"err,omitempty"`
}
STUNServerReport is a single STUN server's result.
type TURNReport ¶
type TURNReport struct {
Servers []TURNServerReport `json:"servers"`
}
TURNReport is the result of the TURN check.
func CheckTURN ¶
CheckTURN ASKS every TURN server FOR AN ALLOCATION.
The question we answer is not "is the server up" but "does it give me a relay address": with wrong credentials, a full quota, or a server listening on TCP only, it looks up yet grants no allocation — and then TURN is of no use at all.
func (TURNReport) Usable ¶
func (r TURNReport) Usable() bool
Usable says whether at least one TURN server granted an allocation.
type TURNServerReport ¶
type TURNServerReport struct {
URL string `json:"url"`
OK bool `json:"ok"`
// RelayedAddr is the relay address the server gave us; that is the
// real proof.
RelayedAddr string `json:"relayed_addr,omitempty"`
MS float64 `json:"ms"`
Err string `json:"err,omitempty"`
}
TURNServerReport is a TURN server's result.
type Verdict ¶
type Verdict string
Verdict is the usability verdict on a relay.
const ( // VerdictUsable means the relay can be used for signaling. VerdictUsable Verdict = "usable" // VerdictUnreachable means the connection could not be established. VerdictUnreachable Verdict = "unreachable" // VerdictAuthRequired means it wants NIP-42 auth; BRIEF says to skip // those. VerdictAuthRequired Verdict = "skip: wants auth" // VerdictRejected means the publish was refused. VerdictRejected Verdict = "skip: publish refused" // VerdictDropsEphemeral means the publish was accepted but the event // never reached the subscriber. VerdictDropsEphemeral Verdict = "skip: drops ephemeral" )
The possible verdicts.