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/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 ¶
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.
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 ¶
This section is empty.
Functions ¶
func IsHomeHost ¶
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 RenderHomePage ¶
RenderHomePage exports renderHomePage (the self-contained alias directory).
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
// 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
// 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{}
// 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
}
Config configures a dmsgweb runtime. Two operating modes:
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.
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 ¶
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 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 ¶
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.