Help us shape the future of Go! Take the 2026 Go Developer Survey

disc

package
v1.3.104-alpha1 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package disc pkg/dmsg/disc/client.go c1-net-dmsg

HTTP-based APIClient. Build-tag-gated to keep net/http out of the js/wasm build graph; the EntryReader/EntryWriter/APIClient interfaces live in interface.go (no build tag) so consumers like pkg/visor/visorconfig still see the type names under GOOS=js without dragging net/http in. See interface.go for the rationale.

Package disc pkg/dmsg/disc/entry.go c1-net-dmsg

Package disc pkg/dmsg/disc/entry_sign.go c1-net-dmsg

Package disc pkg/dmsg/disc/fallback.go c1-net-dmsg

Build-tag-gated off the WASM path because it imports pkg/logging (which wraps logrus, which transitively pulls encoding/json). FallbackClient is constructed only from pkg/dmsg/dmsgc at visor runtime; the WASM install-page graph doesn't reach it.

FallbackClient wraps two APIClients (primary + fallback) and tries the primary first; on error it transparently retries the same call against the fallback. Used by the visor's dmsg layer when an operator points the visor at a hypervisor-hosted dmsg-discovery proxy as primary, with the canonical public discovery as the safety-net fallback. Means visors stay reachable for lookups even when the hypervisor itself is offline, while still benefiting from the proxy's local-relay shortcut when it is online.

Package disc pkg/dmsg/disc/http_message.go c1-net-dmsg

HTTP-response message types used by the discovery service's HTTP API. Build-tag-gated alongside client.go to keep net/http out of the WASM build graph (see interface.go for rationale).

Package disc pkg/dmsg/disc/interface.go c1-net-dmsg

Holds the cross-build interface definitions (EntryReader, EntryWriter, APIClient) that callers depend on for type-checking regardless of whether they actually instantiate an HTTP client.

The HTTP implementation lives in client.go under //go:build !js; keeping the interfaces in their own file (with no build tag and no net/http import) means consumers like pkg/visor/visorconfig — which only need *Entry plus the interface shapes for type aliasing — can compile under GOOS=js GOARCH=wasm (TinyGo included) without the WASM build graph pulling in net/http and tripping over TinyGo 0.41.1's net/http stdlib bug.

Package disc pkg/dmsg/disc/json.go c1-net-dmsg

Package disc pkg/dmsg/disc/recursion.go c1-net-dmsg

Package disc pkg/dmsg/disc/testing.go c1-net-dmsg

Mock APIClient for tests. Build-tag-gated because it imports net/http (for status-code constants). See interface.go for the rationale on splitting net/http-using code from the WASM build.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrKeyNotFound occurs in case when entry of public key is not found
	ErrKeyNotFound = errors.New("entry of public key is not found")
	// ErrNoAvailableServers occurs when dmsg client cannot find any delegated servers available for the given remote.
	ErrNoAvailableServers = errors.New("no delegated dmsg servers available for remote")
	// ErrUnexpected occurs in case when something unexpected happened
	ErrUnexpected = errors.New("something unexpected happened")
	// ErrUnauthorized occurs in case of invalid signature
	ErrUnauthorized = errors.New("invalid signature")
	// ErrBadInput occurs in case of bad input
	ErrBadInput = errors.New("error bad input")
	// ErrValidationNonZeroSequence occurs in case when new entry has non-zero sequence
	ErrValidationNonZeroSequence = NewEntryValidationError("new entry has non-zero sequence")
	// ErrValidationNilEphemerals occurs in case when entry of client instance has nil ephemeral keys
	ErrValidationNilEphemerals = NewEntryValidationError("entry of client instance has nil ephemeral keys")
	// ErrValidationNilKeys occurs in case when entry Keys is nil
	ErrValidationNilKeys = NewEntryValidationError("entry Keys is nil")
	// ErrValidationNonNilEphemerals occurs in case when entry of server instance has non nil Keys.Ephemerals field
	ErrValidationNonNilEphemerals = NewEntryValidationError("entry of server instance has non nil Keys.Ephemerals field")
	// ErrValidationNoSignature occurs in case when entry has no signature
	ErrValidationNoSignature = NewEntryValidationError("entry has no signature")
	// ErrValidationNoVersion occurs in case when entry has no version
	ErrValidationNoVersion = NewEntryValidationError("entry has no version")
	// ErrValidationNoClientOrServer occurs in case when entry has neither client or server field
	ErrValidationNoClientOrServer = NewEntryValidationError("entry has neither client or server field")
	// ErrValidationWrongSequence occurs in case when sequence field of new entry is not sequence of old entry + 1
	ErrValidationWrongSequence = NewEntryValidationError("sequence field of new entry is not sequence of old entry + 1")
	// ErrValidationWrongTime occurs in case when previous entry timestamp is not set before current entry timestamp
	ErrValidationWrongTime = NewEntryValidationError("advertised entry timestamp is not greater than previous")
	// ErrValidationOutdatedTime occurs when the timestamp provided is not recent enough.
	ErrValidationOutdatedTime = NewEntryValidationError("advertised entry has outdated timestamp")
	// ErrValidationServerAddress occurs in case when client want to advertise wrong Server address
	ErrValidationServerAddress = NewEntryValidationError("advertising localhost listening address is not allowed in production mode")
	// ErrValidationEmptyServerAddress occurs when a server entry is submitted with an empty address.
	ErrValidationEmptyServerAddress = NewEntryValidationError("server address cannot be empty")
	// ErrUnauthorizedNetworkMonitor occurs in case of invalid network monitor key
	ErrUnauthorizedNetworkMonitor = errors.New("invalid network monitor key")
)
View Source
var (
	MsgEntrySet     = HTTPMessage{Code: http.StatusOK, Message: "wrote a new entry"}
	MsgEntryUpdated = HTTPMessage{Code: http.StatusOK, Message: "wrote new entry iteration"}
	MsgEntryDeleted = HTTPMessage{Code: http.StatusOK, Message: "deleted entry"}
)

Exposed Http messages

View Source
var ErrCircularDiscovery = errors.New("dmsg disc: circular lookup — a dmsg-addressed discovery cannot be reached from inside its own dial")

ErrCircularDiscovery is returned when answering a discovery request would require making one: the client is addressed over dmsg, and a dmsg dial is already in progress on this call chain waiting for the very entry being asked for.

It is a real configuration outcome, not a theoretical one. A dmsg-addressed discovery is reachable only because the dial that reaches it is short-cut by a seeded entry for the discovery's own key. Put a `dmsg://` URL in a field the code treats as plain HTTP, or lose the seed, and the short-cut is gone: the lookup dials, the dial looks up, and nothing bounds it. Observed on an Android visor whose config had `dmsg://…` under `dmsg.discovery` instead of `dmsg.discovery_dmsg` — it died with "fatal error: stack overflow" about six seconds into every start, restarting for as long as anyone left it running.

Failing the lookup turns that into an error the caller can log, retry, or surface. It cannot repair the configuration, and does not try to.

Functions

func Copy

func Copy(dst, src *Entry)

Copy performs a deep copy of two entries. It is safe to use with empty entries

func NewEntryValidationError

func NewEntryValidationError(cause string) error

NewEntryValidationError constructs a new validation error.

Types

type APIClient

type APIClient interface {
	EntryReader
	EntryWriter
	AllEntries(ctx context.Context) ([]string, error)
	AllClientsByServer(ctx context.Context) (map[string][]*Entry, error)
	ClientsByServer(ctx context.Context, serverPK cipher.PubKey) ([]*Entry, error)
}

APIClient implements dmsg discovery API client.

func NewCXOServersClient added in v1.3.100

func NewCXOServersClient(primary APIClient, servers ServersFunc) APIClient

NewCXOServersClient fronts primary's AllServers with servers, such as a subscription to dmsg discovery's feed. Everything else, and AllServers when servers has no answer, goes to primary.

func NewFallbackClient

func NewFallbackClient(primary, fallback APIClient, log *logging.Logger) APIClient

NewFallbackClient returns an APIClient that tries `primary` first and falls back to `fallback` whenever primary returns a non-nil error. Both clients must be non-nil.

func NewHTTP

func NewHTTP(address string, client *http.Client, log *logging.Logger) APIClient

NewHTTP constructs a new APIClient that communicates with discovery via http.

Defense-in-depth: an empty address (the operator dropped/blanked the discovery URL to force dmsg-only) must NOT build a host-less httpClient — that turns every request into "GET /dmsg-discovery/...: invalid host address" and spins the dmsg serve-loop retrier indefinitely. Return a no-op client that reports "no servers" cleanly so callers fall back to seeded / dmsg entries instead of hammering an invalid URL.

func NewMock

func NewMock(timeout time.Duration) APIClient

NewMock constructs a new mock APIClient.

type Client

type Client struct {
	// DelegatedServers contains a list of delegated servers represented by their public keys.
	DelegatedServers []cipher.PubKey `json:"delegated_servers"`
}

Client contains parameters for Client instances.

func (*Client) String

func (c *Client) String() string

String implements stringer

type Entry

type Entry struct {
	// The data structure's version.
	Version string `json:"version"`

	// An Entry of a given public key may need to iterate. This is the iteration sequence.
	Sequence uint64 `json:"sequence"`

	// Timestamp of the current iteration.
	Timestamp int64 `json:"timestamp"`

	// Static public key of an instance.
	Static cipher.PubKey `json:"static"`

	// Contains the instance's client meta if it's to be advertised as a DMSG Client.
	Client *Client `json:"client,omitempty"`

	// ClientType the instance's client_type meta if it's to be advertised as a DMSG Client.
	ClientType string `json:"client_type,omitempty"`

	// Contains the instance's server meta if it's to be advertised as a DMSG Server.
	Server *Server `json:"server,omitempty"`

	// Signature for proving authenticity of an Entry.
	Signature string `json:"signature,omitempty"`

	// Protocol is the lib that use for multiplexing.
	Protocol string `json:"protocol,omitempty"`
}

Entry represents a Dmsg Node's entry in the Discovery database.

func NewClientEntry

func NewClientEntry(pubkey cipher.PubKey, sequence uint64, delegatedServers []cipher.PubKey) *Entry

NewClientEntry is a convenience function that returns a valid client entry, but this entry should be signed with the private key before sending it to the server

func NewServerEntry

func NewServerEntry(pk cipher.PubKey, seq uint64, addr string, availableSessions int) *Entry

NewServerEntry constructs a new Server entry.

func (*Entry) Sign

func (e *Entry) Sign(sk cipher.SecKey) error

Sign signs the Entry with the provided SecKey.

func (*Entry) String

func (e *Entry) String() string

func (*Entry) Validate

func (e *Entry) Validate(validateTimestamp bool) error

Validate checks if entry is valid.

func (*Entry) ValidateIteration

func (e *Entry) ValidateIteration(nextEntry *Entry) error

ValidateIteration verifies Entry's Sequence against nextEntry.

func (*Entry) VerifySignature

func (e *Entry) VerifySignature() error

VerifySignature checks if the entry's signature matches its PubKey.

type EntryReader

type EntryReader interface {
	Entry(context.Context, cipher.PubKey) (*Entry, error)
	AvailableServers(context.Context) ([]*Entry, error)
	AllServers(context.Context) ([]*Entry, error)
}

EntryReader provides read-only access to discovery entries.

type EntryValidationError

type EntryValidationError struct {
	Cause string
}

EntryValidationError represents transient error caused by invalid data in Entry

func (EntryValidationError) Error

func (e EntryValidationError) Error() string

type EntryWriter

type EntryWriter interface {
	PostEntry(context.Context, *Entry) error
	PutEntry(context.Context, cipher.SecKey, *Entry) error
	DelEntry(context.Context, *Entry) error
}

EntryWriter provides write access to discovery entries.

type FallbackClient

type FallbackClient struct {
	// contains filtered or unexported fields
}

FallbackClient implements APIClient by delegating to a primary client and falling back to a secondary on error.

func (*FallbackClient) AllClientsByServer

func (f *FallbackClient) AllClientsByServer(ctx context.Context) (map[string][]*Entry, error)

AllClientsByServer tries primary, falls back on error.

func (*FallbackClient) AllEntries

func (f *FallbackClient) AllEntries(ctx context.Context) ([]string, error)

AllEntries tries primary, falls back on error.

func (*FallbackClient) AllServers

func (f *FallbackClient) AllServers(ctx context.Context) ([]*Entry, error)

AllServers tries primary, falls back on error.

func (*FallbackClient) AvailableServers

func (f *FallbackClient) AvailableServers(ctx context.Context) ([]*Entry, error)

AvailableServers tries primary, falls back on error.

func (*FallbackClient) ClientsByServer

func (f *FallbackClient) ClientsByServer(ctx context.Context, serverPK cipher.PubKey) ([]*Entry, error)

ClientsByServer tries primary, falls back on error.

func (*FallbackClient) DelEntry

func (f *FallbackClient) DelEntry(ctx context.Context, entry *Entry) error

DelEntry tries primary, falls back on error.

func (*FallbackClient) Entry

func (f *FallbackClient) Entry(ctx context.Context, pk cipher.PubKey) (*Entry, error)

Entry tries primary, falls back to fallback on error.

func (*FallbackClient) PostEntry

func (f *FallbackClient) PostEntry(ctx context.Context, entry *Entry) error

PostEntry tries primary, falls back on error. Both clients receive the entry on success — the primary's success short-circuits the fallback so we don't double-write in the common case.

func (*FallbackClient) PutEntry

func (f *FallbackClient) PutEntry(ctx context.Context, sk cipher.SecKey, entry *Entry) error

PutEntry tries primary, falls back on error.

type HTTPMessage

type HTTPMessage struct {
	Message string `json:"message"`
	Code    int    `json:"code"`
}

HTTPMessage represents a message to be returned as an http response

func (HTTPMessage) String

func (h HTTPMessage) String() string

type Server

type Server struct {
	// Address is the DMSG Server's reachable endpoint ("host:port").
	// Historically an IPv4 or IPv6 address could go in this field
	// either-or; with the dual-stack-within-record design (see #1525)
	// Address now canonically carries the IPv4 endpoint and
	// AddressV6 carries the IPv6 counterpart. Servers listening on
	// only one family leave the other blank; servers listening on
	// both populate both. Dialers prefer v6 with v4 fallback per
	// RFC 8305.
	Address string `json:"address"`

	// AddressV6 is the optional IPv6 ("[host]:port") endpoint for
	// dual-stack DMSG servers. Empty when the server is v4-only.
	// Backward-compat: older servers omit this field; older clients
	// ignore it; either side absent the field falls through to
	// Address as today.
	AddressV6 string `json:"address_v6,omitempty"`

	// AddressUDP / AddressUDPV6 are the optional QUIC (UDP) endpoints a
	// server also listens on (#2607 dmsg-over-QUIC). A QUIC-speaking server
	// populates these alongside the TCP Address(es) and advertises
	// Entry.Protocol="quic"; QUIC-capable clients dial these for a session
	// with native QUIC stream multiplexing + an unreliable datagram channel,
	// while clients/servers that don't know QUIC ignore the fields and use
	// the TCP Address as today. Fully backward-compatible (omitempty +
	// dual-listen). The UDP port may share the TCP port number.
	AddressUDP   string `json:"address_udp,omitempty"`
	AddressUDPV6 string `json:"address_udp_v6,omitempty"`

	// AddressWS is the optional WebSocket endpoint a server also listens on
	// (dmsg-over-WebSocket). It carries a full ws:// or wss:// URL including
	// path (e.g. "wss://dmsg1.skywire.skycoin.com/dmsg"). A WS connection is
	// a net.Conn, so the session runs the SAME Noise+yamux stack as the TCP
	// Address — WS is purely a browser-reachable / CDN-frontable / restrictive-
	// network transport, orthogonal to Protocol (which still selects the mux).
	// The primary consumer is a dmsg client compiled to js/wasm, which cannot
	// open a raw TCP or UDP socket and so can only reach the mesh over WS.
	// Backward-compatible: omitempty + older clients/servers ignore it.
	AddressWS string `json:"address_ws,omitempty"`

	// AddressWT is the optional WebTransport (HTTP/3-over-QUIC) endpoint a server
	// also listens on (dmsg-over-WebTransport). It carries a full https:// URL
	// including path (e.g. "https://1.2.3.4:8443/dmsg"). Like AddressWS it is a
	// net.Conn-style single stream carrying the SAME Noise+yamux stack — the
	// session is identical, only the underlying transport differs. WebTransport's
	// value over WS is that a browser can reach it by BARE IP with no CA-issued
	// certificate: the browser pins CertHashWT (below) in its WebTransport
	// serverCertificateHashes constructor. Backward-compatible: omitempty + older
	// clients/servers ignore it.
	AddressWT string `json:"address_wt,omitempty"`

	// CertHashWT is the lowercase-hex SHA-256 of the DER of the self-signed ECDSA
	// certificate served on AddressWT. It is the value a browser pins in the
	// WebTransport serverCertificateHashes option to reach AddressWT with no CA.
	// The server rotates its cert (<=14-day validity, browser-enforced) and
	// re-advertises this hash. Empty unless AddressWT is set.
	CertHashWT string `json:"cert_hash_wt,omitempty"`

	// CertHashWTNext is the hash of the certificate the server will serve
	// after its next rotation. A browser pins both, so it keeps connecting
	// across the rotation without looking the server up again. Empty from
	// older servers and when AddressWT is unset.
	CertHashWTNext string `json:"cert_hash_wt_next,omitempty"`

	// AvailableSessions is the number of available sessions that the server can currently accept.
	AvailableSessions int `json:"availableSessions"`

	// ServerType of DMSG Server, be `official` of `community`
	ServerType string `json:"serverType,omitempty"`

	// DHTBootstrap indicates this server runs a DHT full node on port 100
	// and can be used as a Kademlia bootstrap peer by visors.
	DHTBootstrap bool `json:"dht_bootstrap,omitempty"`
}

Server contains parameters for Server instances.

func (*Server) String

func (s *Server) String() string

String implements stringer

type ServersFunc added in v1.3.100

type ServersFunc func(ctx context.Context) ([]*Entry, bool)

ServersFunc lists the registered dmsg servers from a copy the caller holds, or reports false to have the discovery asked instead.

Directories

Path Synopsis
Package metrics pkg/dmsg/disc/metrics/empty.go c1-net-dmsg
Package metrics pkg/dmsg/disc/metrics/empty.go c1-net-dmsg

Jump to

Keyboard shortcuts

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