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

dmsgweb

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: 40 Imported by: 0

Documentation

Overview

Package dmsgweb pkg/dmsgweb/home.go c4-app-web

Package dmsgweb pkg/dmsgweb/runtime.go c4-app-web `.dmsg` (and other configurable) domain suffixes. It exposes:

  • a SOCKS5 proxy that intercepts hosts ending in the configured DomainSuffix and tunnels them directly as DMSG streams
  • a raw TCP bridge for fixed (pk, dmsgPort) → localhost mappings

HTTP bridging was removed; raw TCP forwards bytes transparently and works for HTTP, WebSockets, server-sent events, chunked transfers, and any other protocol the dmsg target speaks. The previous HTTP bridge added URL rewriting and per-target connection pooling but no functional behavior that raw TCP doesn't already provide.

The package accepts an externally-created *dmsg.Client so the runtime can be embedded into either the standalone `skywire dmsg web` command or a visor-hosted application (e.g. a resolver inside skysocks-client). Neither mode owns the client; lifecycle belongs to the caller.

Package dmsgweb pkg/dmsgweb/skynet_via.go c1-net-dmsg

Package dmsgweb pkg/dmsgweb/stats.go c4-app-web

Stats collects per-resolver request counters and surfaces them to consumers via Snapshot(). Intentionally lightweight — atomic counters on the hot path, a short mutex-protected last-error window so the hypervisor UI can display recent failures without hitting the visor logs.

Stats is opt-in: when Config.Stats is nil the runtime skips the bookkeeping entirely, so zero overhead for callers that don't care. The visor layer always wires it up so `skywire cli visor proxies --json` can pipe straight to UI widgets.

Index

Constants

View Source
const DefaultDomainSuffix = ".dmsg"

DefaultDomainSuffix is the TLD treated as DMSG addresses when Config.DomainSuffix is empty. Kept here rather than in a CLI flag default so the visor app gets the same behavior.

View Source
const HomeAlias = "home"

HomeAlias is a reserved resolver label that serves a synthetic directory of every alias this resolver knows — a navigation index for the proxy. It is generated in-process by the resolver and is reachable ONLY through the proxy (e.g. http://home.dmsg/); the visor never serves it on a real port. The label is reserved: it shadows any same-named configured alias.

Variables

View Source
var SkynetViaTypes = []types.Type{types.STCPR, types.WS, types.QUIC, types.STCP}

SkynetViaTypes are the transport types that can dial a visor at a fixed address. sudph and webrtc need the address resolver or signaling to connect.

Functions

func IsHomeHost

func IsHomeHost(host, suffix string) bool

IsHomeHost and RenderHomePage export the home-label check + directory renderer so the wasm-visor's in-tab resolving fetch (browse overlay) serves the same home.dmsg page + alias directory as the socks5 resolving proxy.

func ParseSkynetVia added in v1.3.100

func ParseSkynetVia(s string) (cipher.PubKey, string, error)

ParseSkynetVia splits "<pk>@<host:port>".

func RenderHomePage

func RenderHomePage(aliases map[string]cipher.PubKey, suffix string, localPK cipher.PubKey) []byte

RenderHomePage exports renderHomePage (the self-contained alias directory).

func RenderHomePageWith

func RenderHomePageWith(aliases map[string]cipher.PubKey, suffix string, localPK cipher.PubKey, lists *HomeLists) []byte

RenderHomePageWith is RenderHomePage with the visor lists of its other tabs.

func Run

func Run(ctx context.Context, log *logging.Logger, dmsgC *dmsg.Client, cfg Config) error

Run starts the configured bridges and blocks until ctx is canceled. dmsgC must already be Ready(); its lifecycle is the caller's. Returns the first server error encountered, or ctx.Err() on a clean shutdown.

Types

type Config

type Config struct {
	// DomainSuffix is the domain extension that the SOCKS5 resolver
	// treats as DMSG (e.g. ".dmsg"). Defaults to DefaultDomainSuffix.
	DomainSuffix string

	// WebPorts are the localhost TCP listener ports. Used only in mode 2
	// (fixed-mapping); ignored in SOCKS5 mode. One entry per ResolveAddr.
	WebPorts []uint

	// ProxyAddr is the host the SOCKS5 proxy listens on. Empty means
	// loopback ("127.0.0.1") — the safe default. Set to "0.0.0.0" or a
	// specific LAN IP to serve OTHER devices on the network, e.g. a board
	// acting as a `.dmsg` gateway for a home router's LAN. Binding a
	// non-loopback address exposes the proxy to the LAN; only do so on a
	// trusted network.
	ProxyAddr string
	// ProxyPort is the SOCKS5 proxy listener port. Zero disables the
	// SOCKS5 proxy (useful when the runtime is embedded inside an
	// app that already provides its own SOCKS5 front-end).
	ProxyPort uint

	// ResolveAddr is a parallel slice of (pk, dmsgPort) fixed targets.
	// Enables mode 2; must be the same length as WebPorts when set.
	ResolveAddr []DmsgTarget

	// UpstreamSOCKS, when non-empty, sends non-matching CONNECT
	// requests through this upstream SOCKS5 (e.g. "127.0.0.1:1080").
	// Matches the existing `--addproxy` CLI flag.
	UpstreamSOCKS string
	// UpstreamRules send chosen domains to a different upstream, or
	// "direct", ahead of UpstreamSOCKS.
	UpstreamRules []proxyroute.Rule

	// Requests, when non-nil, records every .dmsg dial with the path it took,
	// for the status pages. The visor shares one log across its resolvers.
	Requests *proxystatus.RequestLog
	// Stats, when non-nil, is updated for every SOCKS5 dial. The visor
	// layer allocates one per resolver lifetime so counters persist
	// across Start/Stop cycles.
	Stats *Stats

	// TLSMITM enables on-the-fly TLS termination for browser
	// connections to <pk>.dmsg on the TLSPort, using LeafMinter to
	// produce per-host leaf certs signed by a locally-installed CA.
	// When false the runtime behaves exactly as before — pure SOCKS5
	// byte splice. See pkg/skynetca for the trust model.
	TLSMITM bool
	// TLSPort selects the destination port treated as TLS for MITM.
	// Defaults to 443. Ignored when TLSMITM is false.
	TLSPort uint16
	// TLSUpstreamPort is the destination port a MITM'd TLS connection is
	// sent to once decrypted. Defaults to 80, where visors serve HTTP.
	TLSUpstreamPort uint16
	// LeafMinter mints per-host leaf certs. Required when TLSMITM
	// is true; ignored otherwise.
	LeafMinter skynetca.LeafMinter

	// LocalPK is this visor's public key, used to detect self-lookups for the
	// SelfLoopback short-circuit.
	LocalPK cipher.PubKey
	// SelfLoopback, when true (the default), serves a request whose destination
	// is LocalPK from the local service in-process via SelfDial, instead of
	// dialing out over dmsg back to self (a wasteful, 202-prone round-trip).
	// Set false to force the full transport path — e.g. to test self-transports.
	SelfLoopback bool
	// SelfDial returns an in-process connection to the local service registered
	// on the given dmsg port. Used only when SelfLoopback short-circuits a
	// self-destined request. Nil disables the short-circuit.
	SelfDial func(port uint16) (net.Conn, error)
	// Aliases maps a destination label to a PK (e.g. "skywire" -> LocalPK), so
	// "skywire.dmsg" resolves exactly like "<pk>.dmsg".
	Aliases map[string]cipher.PubKey

	// HomePage, when set, renders the home directory instead of the alias
	// list alone, so the proxy shows the same page as the visor.
	HomePage func(suffix string) []byte

	// DirectClient is an optional dmsg client that reaches servers by their
	// configured address rather than via discovery. A dest in DirectServerPKs
	// is dialed through it by self-rendezvous (EnsureAndObtainSession(dest) +
	// DialStream(dest:port)), which makes non-discovery dmsg SERVERS — they
	// serve /health etc. over dmsg but never register a client entry, so the
	// discovery client cannot resolve them — reachable by name. Nil disables
	// this path (dests fall through to the normal discovery dial).
	DirectClient *dmsg.Client
	// DirectServerPKs is the set of destination PKs to dial via DirectClient.
	DirectServerPKs map[cipher.PubKey]struct{}
	// SkynetDial, when set, is tried before the dmsg dial for an ordinary
	// .dmsg destination: it reaches the peer over the visor's skynet
	// transports (direct, else through a peer that has one), and returns an
	// error to fall back to dmsg. The visor sets it only when this resolver
	// runs under the visor's own key, whose transports those are.
	SkynetDial func(ctx context.Context, pk cipher.PubKey, port uint16) (net.Conn, error)

	// StatusProvider, when non-nil, enables the reserved in-process status hosts
	// served through this proxy — a read-only diagnostic page (logs +
	// route/transport events + per-leg mux view) for a surface. Nil disables the
	// status hosts (they fall through to a normal resolve / upstream forward).
	// See pkg/proxystatus.
	StatusProvider proxystatus.Provider
	// StatusSurface scopes which reserved status host THIS proxy layer owns and
	// answers for (e.g. SurfaceDmsg → only http(s)://status.dmsg/). A status host
	// matching a DIFFERENT surface is NOT served here — it falls through so the
	// request continues up the proxy chain to the layer that owns it. Empty means
	// "serve any matched surface" (the pre-scoping behavior), kept so standalone
	// runtimes and tests need no wiring. Ignored when StatusProvider is nil.
	StatusSurface proxystatus.Surface

	// Publish, when non-nil, is called once with a handler that serves ONE
	// already-established connection exactly as the listener would, protocol
	// sniff included. The visor registers it as an in-process local service so
	// an app on this visor reaches this resolver over the data plane it already
	// holds, with no second SOCKS hop on localhost. The handler stays valid
	// until Run returns.
	Publish func(serve func(net.Conn))
}

Config configures a dmsgweb runtime. Two operating modes:

  1. ResolveAddr empty → SOCKS5 resolver mode. Any hostname ending in DomainSuffix is treated as <pk>.dmsg:<port> and tunneled directly as a DMSG stream. The SOCKS5 proxy on ProxyPort rewrites the dial target to localhost so curl/browsers reach .dmsg sites transparently.

  2. ResolveAddr non-empty → fixed mapping mode. Each entry maps to WebPorts[i] and is served as a raw TCP tunnel to (pk, dmsgPort). The SOCKS5 proxy is disabled — callers point their client directly at 127.0.0.1:WebPorts[i]. Raw TCP transparently carries HTTP, WebSockets, and any other protocol.

type DmsgTarget

type DmsgTarget struct {
	PK   cipher.PubKey
	Port uint16
}

DmsgTarget is a (publicKey, dmsgPort) pair used in fixed-mapping mode.

func ParseDmsgTarget

func ParseDmsgTarget(s string) (DmsgTarget, error)

ParseDmsgTarget parses a "<pk>[:<port>]" string. If the port is omitted, it defaults to 80 (matching the existing CLI behavior).

type HomeLists

type HomeLists struct {
	// Managed are the visors this one manages as a hypervisor, shown with
	// their sites only to the visor's own user.
	Managed []cipher.PubKey
	// Visors are every visor the deployment knows.
	Visors []cipher.PubKey
}

HomeLists are the visors the directory links besides the resolver's names.

type SkynetVia added in v1.3.100

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

SkynetVia holds one transport from this proxy's key to a visor and reaches other visors through it as a one-hop relay. The relay forwards a SYN signed by this key, so the destination sees this key and not the visor's.

func StartSkynetVia added in v1.3.100

func StartSkynetVia(ctx context.Context, log *logging.Logger, mLog *logging.MasterLogger, pk cipher.PubKey, sk cipher.SecKey, visorPK cipher.PubKey, addr string, tpType types.Type) (*SkynetVia, error)

StartSkynetVia opens a transport of type tpType to visorPK at addr, the visor's transport_port (or its stcpr port, or :7777 for stcp), and keeps it open until ctx is done. This side registers nothing anywhere and binds its own listener to loopback only.

func (*SkynetVia) Dial added in v1.3.100

func (s *SkynetVia) Dial(ctx context.Context, pk cipher.PubKey, port uint16) (net.Conn, error)

Dial reaches pk:port over skynet: directly when pk is the visor, else relayed through it. It fails when the visor has no non-dmsg transport to pk.

func (*SkynetVia) DmsgClient added in v1.3.100

func (s *SkynetVia) DmsgClient(ctx context.Context, log *logging.Logger, pk cipher.PubKey, sk cipher.SecKey) (*dmsg.Client, func(), error)

DmsgClient starts a dmsg client that holds its one session through the visor's dmsg relay, over this transport. It registers nothing, and its streams go out over the visor's dmsg server sessions under this key.

type Stats

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

Stats is the public stats container. Safe for concurrent use.

func NewStats

func NewStats() *Stats

NewStats returns a zero-valued Stats with its "started at" clock set to now. Callers typically allocate one per resolver lifetime and pass it to every Config, so cumulative counters survive Start/Stop cycles.

func (*Stats) RecordRequest

func (s *Stats) RecordRequest() func(err error)

RecordRequest begins tracking a new request. Returns a closure that the caller defers to record completion; pass in the request error (nil on success). Pattern:

done := stats.RecordRequest()
// ... do the work ...
done(err)

Nil receiver is a no-op, so callers can unconditionally call it without checking for nil Stats.

func (*Stats) Snapshot

func (s *Stats) Snapshot() StatsSnapshot

Snapshot returns a stable point-in-time copy.

type StatsSnapshot

type StatsSnapshot struct {
	StartedAt     time.Time  `json:"started_at,omitempty"`
	UptimeSec     int64      `json:"uptime_sec,omitempty"`
	TotalRequests uint64     `json:"total_requests"`
	Successful    uint64     `json:"successful"`
	Failed        uint64     `json:"failed"`
	Active        int64      `json:"active"`
	LastRequestAt *time.Time `json:"last_request_at,omitempty"`
	LastSuccessAt *time.Time `json:"last_success_at,omitempty"`
	LastFailureAt *time.Time `json:"last_failure_at,omitempty"`
	LastError     string     `json:"last_error,omitempty"`
}

StatsSnapshot is the read-only view exposed to RPC / UI layers. Safe to marshal.

Jump to

Keyboard shortcuts

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