doctor

package
v0.0.0-...-a9a2de0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 17 Imported by: 0

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:

  1. the NIP-11 information document — does it want auth, what are the limits?
  2. the WebSocket connection — is it reachable, and how fast?
  3. 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

View Source
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.

View Source
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

func CheckRelays(ctx context.Context, urls []string, log *slog.Logger, opts Options) []RelayReport

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

func (r Report) OK() bool

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

func CheckTURN(ctx context.Context, cfg ice.Config, opts Options) TURNReport

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址