Documentation
¶
Overview ¶
Package visorconfig pkg/visor/visorconfig/args.go c3-vis-core
Renders the per-app Args slice as a single shell-like string in the on-disk config file. The hand-edit experience is the goal:
"args": "skycoin daemon --port 6420 --data-dir /var/skycoin/sky"
is far easier to read and tweak than the array form. The Go internals (launcher, RPC, HTTP API) keep []string — only the JSON file representation changes. Old configs with array-form args still load.
The encoding/json-using halves (appsList.MarshalJSON, appsList.UnmarshalJSON, unmarshalAppConfig) live in args_native.go under //go:build !js. This file keeps the type definitions and the json-free shell-arg parser so the WASM build path can still reach SplitArgs, splitArgs, and joinArgs (none of which touch encoding/json).
Package visorconfig pkg/visor/visorconfig/args_native.go c3-vis-core
encoding/json-using halves of args.go. Build-tag-gated off the WASM path because encoding/json's reflect-based marshaller drags reflect.unsafe_New / mapassign / typedmemmove (which TinyGo's stdlib doesn't ship). The browser-side WASM never (de)serializes V1 apps lists; under js V1.Apps stays as raw appsList values and the type's json methods are absent from the build.
Package visorconfig pkg/visor/visorconfig/autoconnect.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/common.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/common_native.go c3-vis-core
Native (non-WASM) implementation of Common.flush — writes a JSON-encoded config back to its on-disk path. encoding/json imports the reflect runtime helpers TinyGo's stdlib doesn't provide, so this code path is tagged off the WASM build to keep `tinygo build -target wasm` viable for the install-page generator. The browser-side caller can't write files anyway.
Package visorconfig pkg/visor/visorconfig/config.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/config_compat.go c3-vis-core backward-compat JSON unmarshaling for renamed config keys.
V1.Pty (canonical) corresponds to operator-config-json's "pty" key. The legacy key "dmsgpty" is still accepted on load so that existing operator config.json files don't break across the rename. Marshaling always emits the canonical "pty" key — V1's struct tag is `json:"pty,omitempty"`.
When both keys appear, the canonical "pty" wins. When neither appears, V1.Pty stays nil (the visor's init path treats nil as "pty subsystem disabled").
Package visorconfig pkg/visor/visorconfig/deployment_services.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/hypervisorconfig.go c3-vis-core
The CookieConfig.SameSite() method (which returns http.SameSite) is the only net/http user in this file; it's moved to hypervisorconfig_native.go under //go:build !js so this file compiles cleanly for js/wasm. See services.go's package doc for the broader rationale.
Package visorconfig pkg/visor/visorconfig/hypervisorconfig_native.go c3-vis-core
Native (non-WASM) implementations for HypervisorConfig methods that touch disk or pull net/http / encoding/json. Kept off the js/wasm build graph so the install-page WASM (genvisor + autoconfigcmd) stays small and TinyGo-compatible.
- CookieConfig.SameSite returns http.SameSite (net/http).
- HypervisorConfig.Parse reads a config file from disk and decodes via encoding/json — useless in a browser, harmful for the WASM dep graph because encoding/json pulls reflect runtime helpers that TinyGo's stdlib lacks.
Package visorconfig pkg/visor/visorconfig/keyring.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/network_watch.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/parse.go c3-vis-core
Parse wraps Reader (from read.go, also !js-tagged) and adds version-compat checking via blang/semver. Both deps (encoding/ json transitively, and blang/semver which imports json itself) stay out of the WASM build. Browser-side WASM never parses an on-disk config; it constructs V1 in memory via genvisor.Generate.
Package visorconfig pkg/visor/visorconfig/preserve.go c3-vis-core
Unknown-key preservation for the config read-modify-write cycle.
The visor loads skywire-config.json into the typed V1 struct and later re-marshals that struct back over the same file (Common.flush). Anything in the file without a matching struct field never survives that round trip: it is not ignored, it is ERASED. Running an older build for a single restart — routine in the dev loop, and the whole of a downgrade — therefore deletes every setting newer than that binary, along with any third-party or experimental key an operator added by hand.
The fix: before overwriting, flush reads the file it is about to replace and walks that JSON against the Go type being marshaled, recording the RESIDUE — the subtrees, at any depth, that have no counterpart field. The marshaled struct is then written with the residue re-attached. Because residue keys are by construction keys the struct does not model, they can never shadow a known key, and clearing a known field still clears it on disk: the struct remains the sole authority for everything it does model.
Reading at flush rather than capturing at load is deliberate. The residue then belongs to the file actually being overwritten, so it survives the config being rebuilt in memory or handed a fresh Common (`skywire cli config update` does exactly that, which is where a load-time capture was observed to fall out), and an operator's hand-edit landing while the visor runs is no longer clobbered by the next flush.
The alternative — json.RawMessage catch-alls with hand-written Marshal/Unmarshal on V1, HypervisorConfig, WasmServeConf, … — was rejected: it needs a new method pair on every nested block anyone might ever extend, and each one is a fresh chance to get the catch-all wrong. This walk covers every block at once and needs no per-type maintenance.
Deliberate omissions:
retiredKeys. A load-time migration that RENAMES a key (V1's legacy "dmsgpty" → "pty", see config_compat.go) leaves the old key looking "unknown". Preserving it would resurrect a key the migration exists to retire, and leave two copies of the same block on disk. Such keys are listed below and dropped from the residue explicitly.
Types with their own json.Unmarshaler / encoding.TextUnmarshaler are opaque: their on-disk shape is not their Go shape, so a field-name walk cannot tell known from unknown inside them. (Duration, cipher.PubKey, and Launcher.Apps' appsList wrapper.)
Array elements re-attach by index and only while the flushed array still has the original length; a resized array drops that array's residue rather than risk grafting a key onto the wrong element.
A config regenerated from scratch (`skywire cli config gen`, `skywire autoconfig`) does not go through flush at all — those paths build V1 in memory and write it with their own os.WriteFile — so regenerating remains the way to clear genuinely stale keys.
Package visorconfig pkg/visor/visorconfig/read.go c3-vis-core
Reader / ReadFile / ReadRaw load a V1 config from disk or an io.Reader. All three pull encoding/json and os.ReadFile, neither of which is reachable from the install-page WASM — browsers don't have filesystems, and TinyGo's stdlib can't link encoding/json's reflect runtime helpers anyway. Build-tag-gated off the WASM path; callers under js construct V1 in memory via genvisor.Generate.
Package visorconfig pkg/visor/visorconfig/resolvers.go c3-vis-core
ResolverConfig declares an ADDITIONAL resolving proxy beyond the two singular blocks (dmsg_web, skynet_web) the visor has always hosted.
Why a list alongside the singular blocks rather than turning dmsg_web into a list: every skywire-config.json in the field carries `dmsg_web` / `skynet_web` objects, and `cli resolver up/down/bind`, the proxy-status pages and the RPC surface all address exactly those two by name. Reshaping them into an array would invalidate every on-disk config and every caller at once for no gain — the two primaries ARE the default resolver pair, and what was missing is the ability to add more. So `resolvers` is purely additive: absent (the case for every config written before this) means exactly today's behavior, and an entry in it is a third, fourth, … proxy that runs beside the primaries.
The knobs are a deliberate subset of DmsgWebConfig/SkynetWebConfig rather than an embedding of either. Embedding would flatten `secret_key` and `forward_proxy` (dmsg-only) into a skynet entry and `route_timeout` (skynet-only) into a dmsg entry, where they would be silently ignored — the kind of config that reads as supported and does nothing. Naming the fields once here, and rejecting the ones that don't apply to an entry's kind at parse time, keeps "what you wrote is what runs" true.
Package visorconfig pkg/visor/visorconfig/services.go c3-vis-core
Services + EnvServices are TYPE ALIASES to the deployment package rather than independent struct definitions. Reason: the visorconfig version was bit-identical to deployment.Services (same fields, same json tags) but lived in two places, which forced a redundant json.Unmarshal at every MakeBaseConfig call to convert the embedded deployment.ServicesJSON into a visorconfig.Services. The alias makes deployment.Prod usable in-place — same memory, same fields, zero conversion — so visorconfig no longer needs to invoke encoding/json on the WASM-clean code path.
HasDmsgEndpoints stays here as a method on the aliased type (works on alias receivers under Go's type-identity rules).
Package visorconfig pkg/visor/visorconfig/services_native.go c3-vis-core
Native (non-WASM) home of the EnvServices alias. Lives here (rather than in services.go) because deployment.EnvServices is itself only defined under !js — its json.RawMessage fields need encoding/json, which is build-tag-gated off the WASM path. See services.go's package doc for context.
This file used to also carry Fetch(), a plain-net/http helper that pulled a Services payload from the conf-service URL. It was removed: the deployment's conf service is dmsg-only (deployment.ProdConf.Conf is a dmsg:// URL), and a bare http.Client can never dial one — it fails with `unsupported protocol scheme "dmsg"`. Standing a dmsg client up here is not possible either: the CLI's dmsg fetch chain lives in cmd/skywire-cli, which imports pkg/visor, which imports this package, so the dependency can only run in that direction. Its sole caller (`skywire cli config update -a`) now uses the same dmsg-first fetch that `config gen` uses.
Package visorconfig pkg/visor/visorconfig/survey_linux.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/survey_whitelist.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/tcp_notsent.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/types.go c3-vis-core
The Duration type's MarshalJSON / UnmarshalJSON live in types_native.go under //go:build !js because encoding/json drags the reflect runtime helpers TinyGo's stdlib lacks. Under js V1 is never (de)serialized, so the absence of those methods causes no functional change.
Package visorconfig pkg/visor/visorconfig/types_native.go c3-vis-core
Duration's JSON codec methods. Tagged off the WASM build because json.Marshal / json.Unmarshal pull the reflect runtime helpers TinyGo's stdlib doesn't provide. See types.go's package doc for the broader rationale.
Package visorconfig pkg/visor/visorconfig/v1.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/values.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/values_linux.go c3-vis-core
Package visorconfig pkg/visor/visorconfig/walletconfig.go c3-app-wallet
Package visorconfig pkg/visor/visorconfig/walletconfig_native.go c3-app-wallet
Index ¶
- Constants
- Variables
- func HTTPAddr() string
- func HomePath() string
- func IsRoot() bool
- func NormalizeResolvers(rs []ResolverConfig)
- func PackageConfig() skyenv.PkgConfig
- func ResolverListenAddr(addr string, port uint) string
- func SkywireConfig() string
- func SplitArgs(s string) ([]string, error)
- func UpdateCommand() []string
- func UseDeploymentSurveyWhitelist() bool
- func UserConfig() skyenv.PkgConfig
- func Version() string
- func WalletCustodyOptions() []string
- type AppSettingsEntry
- type Autoconnect
- type BrowseOriginConfig
- type CoinNodeConfig
- type Common
- type CookieConfig
- type DmsgHTTPServers
- type DmsgHTTPServersData
- type DmsgWebConfig
- type Dmsgscp
- type Duration
- type EnvServices
- type HostBridgeConfig
- type HostBridgeForwardConfig
- type HypervisorConfig
- type Key
- type KeyEntry
- type KeyRing
- type LANDmsgServerConf
- type Launcher
- type LogServer
- type LogStore
- type NetworkPolicy
- type Pty
- type PublicVisorConfig
- type ResolverConfig
- type RewardsConfig
- type Routing
- type Services
- type Skychat
- type SkymailBridgeConfig
- type SkymailConfig
- type SkynetWebConfig
- type Stats
- type Survey
- type TPSDmsgConfig
- type TPVizConfig
- type Transport
- type V1
- func MakeBaseConfig(common *Common, dmsgHTTP bool, services *Services, ...) *V1
- func Parse(log *logging.Logger, r io.Reader, confPath string, ...) (conf *V1, compat bool, err error)
- func ReadFile(confPath string) (*V1, error)
- func ReadRaw(raw []byte, confPath string) (*V1, error)
- func Reader(r io.Reader, confPath string) (*V1, error)
- func (v1 *V1) AddAppConfig(launch *launcher.AppLauncher, appName, binaryName string) error
- func (v1 *V1) ApplyDeploymentServices(next, prev *Services) []string
- func (v1 *V1) AutoconnectSettings() Autoconnect
- func (v1 *V1) DeleteAppArg(launch *launcher.AppLauncher, appName string) error
- func (v1 *V1) DeleteAppConfig(launch *launcher.AppLauncher, appName string) error
- func (v1 *V1) DeriveKeyEntry(label string, index uint32) (KeyEntry, error)
- func (v1 *V1) EffectiveRouteSetupNodes() []cipher.PubKey
- func (v1 *V1) EffectiveSurveyWhitelist() []cipher.PubKey
- func (v1 *V1) EffectiveTransportSetupPKs() []cipher.PubKey
- func (v1 *V1) Flush() error
- func (v1 *V1) GetCalculateRoutes() bool
- func (v1 *V1) GetLogRotationInterval() (Duration, error)
- func (v1 *V1) GetPersistentTransports() ([]tspec.PersistentTransports, error)
- func (v1 *V1) MintKey(label string) (KeyEntry, error)
- func (v *V1) UnmarshalJSON(data []byte) error
- func (v1 *V1) UpdateAppArg(launch *launcher.AppLauncher, appName, argName string, value interface{}) error
- func (v1 *V1) UpdateAppArgBatch(launch *launcher.AppLauncher, appName string, args map[string]any) error
- func (v1 *V1) UpdateAppArgsFull(launch *launcher.AppLauncher, appName string, args []string) error
- func (v1 *V1) UpdateAppAutostart(launch *launcher.AppLauncher, appName string, autoStart bool) error
- func (v1 *V1) UpdateAppEnv(launch *launcher.AppLauncher, appName, key, value string) error
- func (v1 *V1) UpdateAppEnvBatch(launch *launcher.AppLauncher, appName string, env map[string]string) error
- func (v1 *V1) UpdateAppEnvFull(launch *launcher.AppLauncher, appName string, env []string) error
- func (v1 *V1) UpdateAppLauncherMode(launch *launcher.AppLauncher, appName, mode string) error
- func (v1 *V1) UpdateAppPort(launch *launcher.AppLauncher, appName string, port uint16) error
- func (v1 *V1) UpdateAppSettings(vals map[string]map[string]int64, text map[string]map[string]string) error
- func (v1 *V1) UpdateAutoconnect(set func(*Autoconnect)) error
- func (v1 *V1) UpdateCalculateRoutes(enabled bool) error
- func (v1 *V1) UpdateHostBridge(hb *HostBridgeConfig) error
- func (v1 *V1) UpdateHypervisors(pks []cipher.PubKey) error
- func (v1 *V1) UpdateLogRotationInterval(d Duration) error
- func (v1 *V1) UpdateMinHops(hops uint16) error
- func (v1 *V1) UpdateNetworkPolicy(p NetworkPolicy) error
- func (v1 *V1) UpdateNetworkWatch(watch, ipCheck *Duration) error
- func (v1 *V1) UpdatePersistentTransports(pTps []tspec.PersistentTransports) error
- func (v1 *V1) UpdatePtyWhitelist(pks []cipher.PubKey) error
- func (v1 *V1) UpdatePublicAutoconnect(pAc bool) error
- func (v1 *V1) UpdateTCPNotSentLowat(n *int) error
- func (v *V1) ValidateResolvers() error
- func (v *V1) WalletCustody() string
- func (v *V1) WalletDir() string
- func (v *V1) WalletRemotePK() string
- func (v *V1) WalletServe() bool
- type WTPeer
- type WalletConfig
- type WasmServeConf
- type WispConfig
Constants ¶
const ( // Stdin read config from STDIN. Stdin = "STDIN" // Stdout write config to STDOUT. Stdout = "STDOUT" )
const ( // ResolverKindDmsg resolves <pk><suffix> hostnames over dmsg, the same // way the dmsg_web block does. Default when Kind is empty. ResolverKindDmsg = "dmsg" // ResolverKindSkynet resolves them over skywire routing (the router), // the same way the skynet_web block does. ResolverKindSkynet = "skynet" )
Resolver kinds. A ResolverConfig with an empty Kind is a dmsg resolver — the far more common case, and the one that can carry its own identity.
const ( DefaultDmsgWebProxyPort = 4445 DefaultSkynetWebProxyPort = 4446 )
DefaultDmsgWebProxyPort / DefaultSkynetWebProxyPort are the SOCKS5 ports the two primary resolvers bind when their block leaves proxy_port at zero. They live here (not only in pkg/visor) because port-conflict detection has to know what an unset port resolves to — a config that sets an extra resolver to 4445 collides with a dmsg_web block that never mentions 4445 at all.
const ( FileLogStore = "file" MemoryLogStore = "memory" )
LogStore types.
const ( // DefaultTimeout is used for default config generation and if it is not set in config. DefaultTimeout = Duration(10 * time.Second) // DefaultLogRotationInterval is used as default for RotationInterval and if it is not set in Log. DefaultLogRotationInterval = Duration(time.Hour * 24 * 7) )
const ( // WalletCustodyBrowser keeps keys in the browser (localStorage on a native // visor or `hv serve`, an OPFS virtual directory under wasm). The host // never sees them. Available EVERYWHERE, because the wallet frontend is // always a browser SPA — hence the default. WalletCustodyBrowser = "browser" // WalletCustodyDisk keeps wallet files on a real filesystem, served by the // skycoin-web server (RunSkycoinWeb) at Wallet.Dir. Requires a backend Go // process AND a filesystem, so it is offered only on a native visor or // `hv serve` — never in a statically-hosted wasm PWA or a single-file // hypervisor. Flips the trust model: the host holds the keys. WalletCustodyDisk = "disk" // WalletCustodyRemote delegates to another visor's skycoin-web server over // dmsg (Wallet.RemotePK). Available anywhere a dmsg target is reachable; // you are trusting that remote. WalletCustodyRemote = "remote" )
Wallet custody modes — WHERE the keys and wallet files live. This is the one axis the operator actually chooses; how the frontend is served and how the node API is reached vary by platform and are not the operator's concern.
See docs/design/skycoin-web-wallet-architecture.md.
const DefaultAutoconnectSiteLinks = 2
DefaultAutoconnectSiteLinks is how many transports of each site type autoconnect keeps between two sites when SiteLinks is unset.
const DefaultWispPort = 6001
DefaultWispPort is the virtual-loopback port the embedded Wisp server binds and page JS dials, matching `skywire cli wisp serve`'s default so the two are one address in two places rather than two conventions.
const KeyRingTypeDeterministic = "deterministic"
KeyRingTypeDeterministic is the only KeyRing.Type today: keys are derived from the visor secret key (the implicit seed) by a one-way HMAC chain, in the spirit of a skycoin deterministic wallet.
Variables ¶
var ( // DmsgHTTPPort Listening port for dmsghttp logserver. Set to // pkg/dmsg/dmsg.DefaultDmsgHTTPPort's value (80). Hardcoded here // rather than imported so values.go stays WASM-clean — the dmsg // client package pulls in unrelated heavy deps that the // in-browser config-generator doesn't need. DmsgHTTPPort = uint16(80) // PublicVisorMaxTransports is the DISTINCT-PEER count at which a public visor // drain-pauses its service-discovery registration (load-shedding). It counts // unique remote visors, not raw transports — several transport types to the // same peer (stcpr + sudph + squicr, for route diversity) count as one — so // the cap describes real peer load, not transport-type multiplication. It // must sit comfortably above the fleet size, because a public visor that also // runs public_autoconnect peers with ~every other public visor. At 1000 (≈ // the fleet size) the busiest visors oscillated across the cap and flapped // out of the registry, starving browser/wasm clients of a reachable target. // 2048 gives ~2× headroom over the current fleet. PublicVisorMaxTransports = 2048 )
var ( // ErrInvalidSK occurs when config file has an invalid secret key. ErrInvalidSK = errors.New("config has invalid secret key") )
var ( // ErrNoConfigPath is returned on attempt to read/write config when visor contains no config path. ErrNoConfigPath = errors.New("no config path") )
var ( // VisorConfigFile will contain the path to the visor's config or `stdin` to denote that the config was read from STDIN VisorConfigFile string )
Functions ¶
func IsRoot ¶
func IsRoot() bool
IsRoot checks for root permissions.
It resolves the effective UID FIRST (0 == root), which never needs a passwd lookup. On a static (musl) build running under a UID with no /etc/passwd entry, user.Current() returns (nil, err); the previous code ignored the error and dereferenced the nil user, panicking in this package's init() — which crashed EVERY skywire command on such hosts (seen on the prod hv-serve unit, crash-looping v1.3.86). Geteuid is both safer and the canonical root test.
func NormalizeResolvers ¶
func NormalizeResolvers(rs []ResolverConfig)
NormalizeResolvers fills in the defaults a hand-edited config may omit — today just the name, which everything downstream (log fields, app rows) needs to be non-empty and unique. Called from ValidateResolvers so both the config generator and the visor see the same names.
func PackageConfig ¶
PackageConfig returns the package-specific config paths
func ResolverListenAddr ¶
ResolverListenAddr renders a resolving proxy's SOCKS5 listener the way its runtime binds it: an empty addr means loopback, and a zero port means the SOCKS5 front-end is disabled (rendered as empty, never as ":0").
func SkywireConfig ¶
func SkywireConfig() string
SkywireConfig returns the full path to the package config
func SplitArgs ¶
SplitArgs is the exported counterpart to splitArgs — same parser, available to callers outside the package (e.g. config-gen wants to tokenize SKYCOIND_FLAGS using the same rules the on-disk config uses).
func UpdateCommand ¶
func UpdateCommand() []string
UpdateCommand returns the commands which are run when the update button is clicked in the ui
func UseDeploymentSurveyWhitelist ¶
func UseDeploymentSurveyWhitelist() bool
UseDeploymentSurveyWhitelist reports whether the deployment's survey whitelist — the key set the conf service hands out — should be written into this visor's config and refreshed into it thereafter.
Those keys authorize their holders to read this visor's log server, its system survey and its pprof endpoints over dmsg (see initDmsgHTTPLogServer). On a fleet node that is the point: it is how an operator inspects machines they run. The mobile pair says no — see the comment there.
func UserConfig ¶
UserConfig contains installation paths for running skywire as the user
func Version ¶
func Version() string
Version gets the version of the installation for the config.
The runtime version-discovery path (git-describe fallback when buildinfo.Version() returns "unknown") lives in values_native.go behind a !js build tag — it shells out to `git`, which is nonsensical in a browser WASM context. Under js the returned value is just buildinfo.Version() with no fallback.
func WalletCustodyOptions ¶
func WalletCustodyOptions() []string
WalletCustodyOptions lists the custody modes this build can realize — what a GUI should offer. browser and remote are always available; disk appears only where there is a backend process with a filesystem.
Types ¶
type AppSettingsEntry ¶
type AppSettingsEntry struct {
Values map[string]int64 `json:"values,omitempty"`
Text map[string]string `json:"text,omitempty"`
}
AppSettingsEntry is one app's persisted live knobs: the numeric payloads and the list ones, exactly as pkg/skysocks/skysettings encodes them. The config carries them without knowing what any of them mean — a knob added to the client needs no change here.
type Autoconnect ¶
type Autoconnect struct {
// SiteLinks is how many transports of SiteTypes autoconnect keeps between
// two sites. A site is the visors behind one public IP that share a LAN
// subnet. Unset means DefaultAutoconnectSiteLinks; 0 turns the limit off.
SiteLinks *int `json:"site_links,omitempty"`
// SiteTypes are the transport types SiteLinks applies to; empty means sudph.
SiteTypes []string `json:"site_types,omitempty"`
// MaxPerType caps the transports of a type autoconnect dials. A type left
// out keeps its built-in cap; 0 means no cap.
MaxPerType map[string]int `json:"max_per_type,omitempty"`
// MaxTotal caps the transports autoconnect dials of all types; 0 means no cap.
MaxTotal int `json:"max_total,omitempty"`
}
Autoconnect limits the transports public autoconnect dials. Each limit counts only the automatic transports this visor dialed itself, and reaching one stops new dials without closing any transport.
func (Autoconnect) Cap ¶
func (a Autoconnect) Cap(t string, builtin int) int
Cap is the dial cap for transport type t: MaxPerType's entry when set, otherwise builtin. 0 means no cap.
func (Autoconnect) Links ¶
func (a Autoconnect) Links() int
Links is SiteLinks with its default applied.
func (Autoconnect) SiteLimited ¶
func (a Autoconnect) SiteLimited(t string) bool
SiteLimited reports whether the site limit applies to transport type t.
type BrowseOriginConfig ¶
type BrowseOriginConfig struct {
// Enable must be true for the reverse-proxy origin to start.
Enable bool `json:"enable"`
// Mode selects how each mesh site gets its own isolated browser origin:
// "subdomain" (default) — ONE listener on Addr; each site is the origin
// "<vhost>.<base32pk><Suffix>" routed by Host header. Needs *.<Suffix> to
// resolve to loopback (*.mesh.localhost does in every target browser) or,
// for a hosted domain, real wildcard DNS.
// "port" — each site gets its OWN "127.0.0.1:<port>" origin from a small
// pool (PortBase..PortBase+PortSpan). No DNS/wildcard dependency at all;
// works anywhere. Weaker isolation than subdomain (cookies are domain-
// scoped, not port-scoped), so prefer subdomain unless *.localhost can't
// resolve in the target environment.
Mode string `json:"mode,omitempty"`
// Addr is the listen address for subdomain mode, and the "portal" listener
// (GET /open?host=… → 302 to the per-site port) for port mode. Default
// "127.0.0.1:8461". Loopback-only unless you front it with real TLS.
Addr string `json:"addr,omitempty"`
// Suffix is the subdomain-mode origin domain; must start with "." — default
// ".mesh.localhost" (loopback, no public cert needed). Set to a wildcard
// domain you control (e.g. ".haltingstate.net") or your own domain to serve
// a hosted browse origin behind real TLS.
Suffix string `json:"suffix,omitempty"`
// TLSCert / TLSKey, when BOTH set, serve the browse origin over HTTPS with
// this cert/key — a real cert for <Suffix>, or a locally-trusted one (e.g.
// from mkcert) for local https parity with the hosted origin (same scheme,
// real secure-context, real-domain cookie isolation). Empty = plain HTTP
// (fine for *.mesh.localhost, which is a secure context without TLS).
TLSCert string `json:"tls_cert,omitempty"`
TLSKey string `json:"tls_key,omitempty"`
// PortBase / PortSpan bound the per-site loopback port pool used in "port"
// mode (least-recently-used eviction when full). Defaults 8470 / 64.
PortBase int `json:"port_base,omitempty"`
PortSpan int `json:"port_span,omitempty"`
}
BrowseOriginConfig enables the native "real-origin" browse proxy: a loopback HTTP reverse-proxy origin that serves mesh (dmsg) sites so the BROWSER does native subresource loading, cookies, redirects, WASM and streaming — skywire only proxies the transport over dmsg (see pkg/visor/meshproxy.go and pkg/wasmhv/browseui/RFC-real-origin-browser.md). Content flows through THIS visor's OWN transports; it is a local listener, never a shared/central relay.
The browse iframe points at http://<vhost>.<base32pk><suffix>:<port>/, which the browser resolves to loopback (for .mesh.localhost) or to wherever the operator's DNS points (a hosted domain). This is the native counterpart of the wasm surface's Service-Worker browse origin.
type CoinNodeConfig ¶
type CoinNodeConfig struct {
// LocalAddr is the node's HTTP API address on this host
// (e.g. "127.0.0.1:6420" — the skycoin daemon default). The visor
// reverse-proxies this over dmsg and probes <LocalAddr>/api/v1/health.
LocalAddr string `json:"local_addr"`
// DmsgPort is the dmsg port the node is exposed on. The advertised SD
// entry address is <this-visor-PK>:<DmsgPort>, which a wallet dials
// through the mesh (or the wasm visor's HTTP-over-dmsg shim).
DmsgPort uint16 `json:"dmsg_port"`
// Whitelist, when non-empty, restricts which peer PKs may reach the
// forwarded node. Empty (default) = any authenticated peer — the usual
// choice for a public read-mostly coin API.
Whitelist []cipher.PubKey `json:"whitelist,omitempty"`
}
CoinNodeConfig describes one fibercoin node this visor exposes over dmsg and advertises in service discovery (servicedisc.ServiceTypeCoin). The node (e.g. `skywire skycoin daemon`) runs INDEPENDENTLY of the visor and serves a plain HTTP API on localhost; the visor merely reverse-proxies it over the mesh and, on the SD heartbeat interval, reads its /api/v1/health to build a truthful, DETECTED CoinInfo advertisement (never operator-claimed identity).
type Common ¶
type Common struct {
Version string `json:"version"`
SK cipher.SecKey `json:"sk,omitempty"`
PK cipher.PubKey `json:"pk,omitempty"`
// contains filtered or unexported fields
}
Common represents the common fields that are shared across all config versions, alongside logging and flushing fields.
func (*Common) MasterLogger ¶
func (c *Common) MasterLogger() *logging.MasterLogger
MasterLogger returns the underlying master logger.
func (*Common) Path ¶
Path returns the on-disk path the config was loaded from. Empty when the config came from STDIN or was synthesized in-memory.
func (*Common) SetAfterFlush ¶
func (c *Common) SetAfterFlush(f func())
SetAfterFlush registers f to run after each write of the config file. Set it before anything can flush. f must not lock the config.
func (*Common) SetLogger ¶
func (c *Common) SetLogger(log *logging.MasterLogger)
SetLogger sets logger.
type CookieConfig ¶
type CookieConfig struct {
HashKey Key `json:"hash_key"` // Signs the cookie: 32 or 64 bytes.
BlockKey Key `json:"block_key"` // Encrypts the cookie: 16 (AES-128), 24 (AES-192), 32 (AES-256) bytes. (optional)
ExpiresDuration time.Duration `json:"expires_duration"` // Used for determining the 'expires' value for cookies.
Path string `json:"path"` // optional
Domain string `json:"domain"` // optional
TLS bool `json:"-"`
}
CookieConfig configures cookies used for hypervisor.
func (*CookieConfig) FillDefaults ¶
func (c *CookieConfig) FillDefaults()
FillDefaults fills config with default values.
func (*CookieConfig) HTTPOnly ¶
func (c *CookieConfig) HTTPOnly() bool
HTTPOnly gets cookie's `HTTPOnly` value.
Unconditionally true: the session cookie is never read from JavaScript, so keeping it out of document.cookie costs nothing and blocks XSS-based session theft. It used to return !c.TLS, which inverted the protection exactly when an operator opted into TLS — the safer configuration turned HTTPOnly off.
func (*CookieConfig) SameSite ¶
func (c *CookieConfig) SameSite() http.SameSite
SameSite gets cookie's `SameSite` value.
func (*CookieConfig) Secure ¶
func (c *CookieConfig) Secure() bool
Secure gets cookie's `Secure` value.
type DmsgHTTPServers ¶
type DmsgHTTPServers struct {
Prod DmsgHTTPServersData `json:"prod"`
}
DmsgHTTPServers struct use to unmarshal dmsghttp file
type DmsgHTTPServersData ¶
type DmsgHTTPServersData struct {
DMSGServers []*disc.Entry `json:"dmsg_servers"`
DMSGDiscovery string `json:"dmsg_discovery"`
TransportDiscovery string `json:"transport_discovery"`
AddressResolver string `json:"address_resolver"`
RouteFinder string `json:"route_finder"`
ServiceDiscovery string `json:"service_discovery"`
}
DmsgHTTPServersData is a part of DmsgHTTPServers
type DmsgWebConfig ¶
type DmsgWebConfig struct {
// Enable must be true for the resolver to start.
Enable bool `json:"enable"`
// SecretKey runs the resolver under its OWN dmsg identity instead of the
// visor's. Unset (the default) keeps today's behavior: the resolver
// borrows v.dmsgC and answers as the visor.
//
// It exists because some keys cannot move. The survey whitelist knows the
// key the resolving proxy runs under, so rotating it means updating the
// deployment; but running a second FULL dmsg client to hold that key
// costs its own sessions to every server and its own discovery entry,
// re-registered on every restart — the identity churn that got the tpviz
// throwaway client removed in #4500/#4501.
//
// Set here, the resolver's client is attached IN-PROCESS to the visor's
// own relay (dmsg.AttachInProcess): one session, over an in-memory pipe,
// no discovery entry, and every stream carried by the visor's sessions
// and transports. The host pays for dmsg once and the key never rotates.
//
// Nothing dials this key inbound except through the visor, so it suits an
// outbound resolver and NOT a service that must be reachable on its own.
SecretKey *cipher.SecKey `json:"secret_key,omitempty"`
// ProxyPort is the local SOCKS5 listener. Default 4445.
ProxyPort uint `json:"proxy_port,omitempty"`
// ProxyAddr is the host the SOCKS5 proxy binds to. Empty = loopback
// ("127.0.0.1"). Set to "0.0.0.0" or a LAN IP to serve OTHER devices on
// the network — e.g. a board acting as a `.dmsg` gateway for a home
// router's LAN. Only bind a non-loopback address on a trusted network.
ProxyAddr string `json:"proxy_addr,omitempty"`
// WebPort is the local HTTP bridge the SOCKS5 proxy rewrites
// matched hosts to. Default 8080.
WebPort uint `json:"web_port,omitempty"`
// DomainSuffix is the TLD treated as DMSG addresses. Default ".dmsg".
DomainSuffix string `json:"domain_suffix,omitempty"`
// UpstreamSOCKS, if set, forwards non-matching CONNECTs to this
// upstream SOCKS5 server (e.g. "127.0.0.1:1080" for a chained
// skysocks-client). Empty = direct connect.
UpstreamSOCKS string `json:"upstream_socks,omitempty"`
// UpstreamRules send chosen domains to another upstream (a second
// skysocks-client, say) or "direct", ahead of UpstreamSOCKS. The longest
// matching suffix wins.
UpstreamRules []proxyroute.Rule `json:"upstream_rules,omitempty"`
// TLSMITM enables on-the-fly TLS termination for browser
// connections to <pk>.dmsg on TLSPort, using a leaf cert minted
// from a locally-installed CA at TLSCAPath/TLSCAKeyPath. Off by
// default; when off the resolver behaves exactly as before.
// See pkg/skynetca and docs/skynet-tls.md for the trust model.
TLSMITM bool `json:"tls_mitm,omitempty"`
// TLSPort is the destination port treated as TLS. Default 443.
TLSPort uint16 `json:"tls_port,omitempty"`
// TLSUpstreamPort is the destination port a MITM'd connection is sent
// to once decrypted. Default 80, where visors serve HTTP.
TLSUpstreamPort uint16 `json:"tls_upstream_port,omitempty"`
// TLSCAPath / TLSCAKeyPath point to a CA generated by
// `skywire cli resolver ca gen`. Both required when TLSMITM
// is true. The visor logs and falls back to non-MITM mode if
// either is missing or unreadable; it does not refuse to start.
TLSCAPath string `json:"tls_ca_path,omitempty"`
TLSCAKeyPath string `json:"tls_ca_key_path,omitempty"`
// SelfLoopback, when nil or true (the default), serves a request whose
// destination is THIS visor's own PK from the local service in-process
// (net.Pipe) instead of dialing out over dmsg back to self — which dmsg
// does not loopback and which is 202-prone. Set false to exercise the
// full self-dial transport path (testing self-reachability).
SelfLoopback *bool `json:"self_loopback,omitempty"`
// Alias is a friendly hostname label for THIS visor's own PK, so
// "<alias>.dmsg" resolves to the local visor without hardcoding its
// public key. Defaults to "skywire" when unset (→ "skywire.dmsg").
Alias string `json:"alias,omitempty"`
// SelfLoopbackAuthenticated, when nil or true (the default), makes a
// self-loopback request (skywire.dmsg / <self-pk>.dmsg served in-process
// via SelfDial) present THIS visor's own PK as the authenticated caller.
// The local visor's PK is always on its own landing-page whitelist, so the
// in-process self view renders the same authenticated endpoints (pprof,
// /visor.log, …) a survey-whitelisted remote peer sees — local self-access
// through one's own proxy is inherently authorized. Set false to render the
// self-loopback landing page as an anonymous (unauthenticated) caller.
SelfLoopbackAuthenticated *bool `json:"self_loopback_authenticated,omitempty"`
// ForwardProxy, when true, makes the visor serve a CLEARNET HTTP forward-
// proxy over dmsg on ForwardPort: a whitelisted peer (e.g. a browser
// wasm-visor that cannot itself do TLS) sends an absolute-URL HTTP request
// over dmsg and this visor fetches it from the clearnet and returns the
// response. Default off. Access is restricted to the same whitelist that
// gates the visor's other privileged dmsg surfaces (own PK + hypervisors +
// survey/pty whitelist) — it is NOT an open relay. Egress prefers
// UpstreamSOCKS when set (so the exit IP is the skysocks node, not this
// visor); only if UpstreamSOCKS is empty does this visor connect directly
// (its own IP becomes the exit). See docs/design/dmsg-forward-proxy.md.
ForwardProxy bool `json:"forward_proxy,omitempty"`
// ForwardPort is the dmsg port the forward-proxy listens on. Default 84.
ForwardPort uint16 `json:"forward_port,omitempty"`
}
DmsgWebConfig enables the embedded `.dmsg` resolving proxy hosted by the visor. When Enable is true, the visor serves a SOCKS5 proxy on ProxyPort plus an HTTP bridge on WebPort; browsers pointed at the SOCKS5 proxy can load http://somekey.dmsg URLs directly. Reuses the visor's own dmsg client for the outbound fetches, so no extra dmsg identity or session load is incurred.
This is the in-process version of the `skywire dmsg web` CLI, so the same domain-suffix resolving behavior is available without running a separate utility alongside the visor.
type Dmsgscp ¶
type Dmsgscp struct {
// Disabled, when true, suppresses both the dmsg and skynet
// listeners. Default false (== on). Inverted from the original
// `Enabled` field so the zero value matches the default.
Disabled bool `json:"disabled,omitempty"`
// DmsgPort is the dmsg port to listen on; zero means use
// dmsgscp.DefaultPort (23). The skynet mirror binds the same
// port number on the router for parity.
DmsgPort uint16 `json:"dmsg_port,omitempty"`
// RootDir is the filesystem directory the host serves files
// from (its effective chroot). Empty means
// <local_path>/scp-root is used.
RootDir string `json:"root_dir,omitempty"`
// Whitelist optionally overrides the dmsgpty whitelist. When
// nil or empty, init reuses Pty.Whitelist.
Whitelist []cipher.PubKey `json:"whitelist,omitempty"`
}
Dmsgscp configures the dmsgscp-host (scp-over-dmsg daemon). The host is on by default — access is gated by the same whitelist that dmsgpty uses (a peer's PK must be on the list to be served). Operators who want to disable it entirely set Disabled=true. When Whitelist is empty the host reuses Pty.Whitelist so trusting a peer for pty implicitly trusts them for file transfer too.
The host listens on BOTH dmsg and the skywire router (skynet) at the same port number — same dual-listen pattern dmsg_http and others use. dmsg covers the bootstrap path (works before any router transport is up); skynet covers steady-state operation over arbitrary transports.
type Duration ¶
Duration wraps around time.Duration to allow parsing from and to JSON.
The Marshal/Unmarshal methods live in types_native.go (!js) — browsers never (de)serialize V1, so dropping the methods under js doesn't change observable behavior; it just frees TinyGo from linking encoding/json's reflect path.
func (Duration) MarshalJSON ¶
MarshalJSON implements json marshaling.
func (*Duration) UnmarshalJSON ¶
UnmarshalJSON implements unmarshal from json.
type EnvServices ¶
type EnvServices = deployment.EnvServices
EnvServices is the wrapper struct for the outer JSON. Aliased to deployment.EnvServices (only defined under !js — see deployment's config.go package doc for the WASM-stripping rationale). Consumers using visorconfig.EnvServices keep working under non-WASM builds.
type HostBridgeConfig ¶ added in v1.3.101
type HostBridgeConfig struct {
// Secret is the pairing secret the bridge gave this visor. It comes first
// so the runtime-config view can drop its line. See redactSecrets.
Secret string `json:"secret,omitempty"`
// URL is the one the bridge prints, token included.
URL string `json:"url"`
Forwards []HostBridgeForwardConfig `json:"forwards"`
}
HostBridgeConfig is the host bridge a browser visor uses for the sockets of its host, kept so the visor reconnects to it at boot. See pkg/hostbridge and `skywire cli visor hostbridge`.
type HostBridgeForwardConfig ¶ added in v1.3.101
HostBridgeForwardConfig passes connections to Host, on the bridge's host, to Local, an address the visor dials.
type HypervisorConfig ¶
type HypervisorConfig struct {
Enable bool `json:"enable"` // Whether the hypervisor is enabled (starts DMSG RPC + tracking + web UI on visor startup).
// DmsgIngest is read by MOBILE builds only: it opts in to the remote-visor
// dmsg RPC ingest — the listener other visors connect to when this PK is in
// their `hypervisors` list (the phone app's Fleet toggle). Off by default so
// the mobile core stays an API-only localhost surface. Desktop builds ignore
// it: there the ingest always starts with the hypervisor, as before.
DmsgIngest bool `json:"dmsg_ingest,omitempty"`
// UIDisable, when true, suppresses ONLY the web UI (HTTP) server while the
// hypervisor's DMSG-RPC listener + managed-visor tracking (and `hv ls` over
// the visor RPC) stay active. Toggled at runtime with `hv ui enable/disable`.
// Default false (UI served) — backward-compatible with configs that omit it.
UIDisable bool `json:"ui_disable,omitempty"`
// DeskAddr is where the desk — the wasm-visor hypervisor UI, hosted out of
// the skywire command module — is served: a second listener carrying the
// same handler as HTTPAddr, so the API, the login and every page are the
// same on both; only the root differs. The root of HTTPAddr is the Angular
// dashboard, the root of DeskAddr is the desk. Set from HVDESKADDR in
// skywire.conf or `config gen --hvdeskaddr`; empty takes the platform
// default (:8010 native, none in a browser). No listener is started when
// the build has no command module to host a desk out of.
DeskAddr string `json:"desk_addr"`
UIAssets fs.FS `json:"-"`
PK cipher.PubKey `json:"-"`
SK cipher.SecKey `json:"-"`
DBPath string `json:"db_path"` // Path to store database file.
EnableAuth bool `json:"enable_auth"` // Whether to enable user management.
// EnablePKEndpoint, when true, exposes an unauthenticated
// GET /api/pk route returning the hypervisor's pubkey. Off by
// default — only the skybian / Arch-linux-ARM image builds
// flip it on so first-boot visors can discover the bundled
// hypervisor's pubkey without an account. The route still
// requires an SW-Public header naming the caller's PK as a
// soft "looks like another visor" gate; see
// hypervisor_handlers_misc.go. No omitempty — the field is
// always serialized so operators see it in the generated
// config and know it exists.
EnablePKEndpoint bool `json:"enable_pk_endpoint"`
Cookies CookieConfig `json:"cookies"` // Configures cookies (for session management).
DmsgDiscovery string `json:"-"` // Dmsg discovery address.
DmsgPort uint16 `json:"dmsg_port,omitempty"` // Dmsg port to serve the (gob) management RPC on.
HTTPAddr string `json:"http_addr"` // HTTP address to serve API/web UI on.
// DmsgUIPort, when non-zero, also serves the hypervisor HTTP API + web UI
// over a dmsg listener on this port — so a browser dmsg client (the WASM
// hypervisor UI) can reach the fleet BY PUBLIC KEY with no exposed HTTP
// port, end-to-end over dmsg. Off by default (0): exposing fleet control
// over dmsg is opt-in. The handler's own login/CSRF auth still applies; the
// dmsg Noise layer additionally authenticates the caller's PK.
DmsgUIPort uint16 `json:"dmsg_ui_port,omitempty"`
EnableTLS bool `json:"enable_tls"` // Whether to enable TLS.
TLSCertFile string `json:"tls_cert_file"` // TLS cert file location.
TLSKeyFile string `json:"tls_key_file"` // TLS key file location.
TPViz TPVizConfig `json:"tp_viz"` // Transport visualizer config.
LANDmsgServer *LANDmsgServerConf `json:"lan_dmsg_server,omitempty"` // LAN DMSG server config.
// CXOSubscribeInterval is the resync floor for the on-demand
// CXO subscription manager. Subscriptions stay open while a UI
// tab is acquired; the manager won't tear down a feed sooner
// than this after its last release. 0 falls back to the
// package default (5m). Common knob to tune the bandwidth /
// freshness tradeoff for hvui tabs that source CXO data.
CXOSubscribeInterval Duration `json:"cxo_subscribe_interval,omitempty"`
// WasmServe, when non-nil with a non-empty Addr, also serves the
// standalone wasm-visor PWA / testing harness from this same visor
// process on that address, so a rebuild-restart serves the latest (no
// separate `hv serve`). Off by default. See pkg/visor.ServeWasm.
WasmServe *WasmServeConf `json:"wasm_serve,omitempty"`
// WasmModuleSource is the visor the js/wasm module served for the desk
// is refreshed from when the desk is opened. Empty means
// the deployment's wasm_module_source.
WasmModuleSource string `json:"wasm_module_source,omitempty"`
}
HypervisorConfig configures the hypervisor.
func DefaultHypervisorConfig ¶
func DefaultHypervisorConfig() HypervisorConfig
DefaultHypervisorConfig returns a HypervisorConfig with sensible defaults. Used when enabling the hypervisor at runtime on a visor that wasn't configured as a hypervisor at startup.
func GenerateWorkDirConfig ¶
func GenerateWorkDirConfig() HypervisorConfig
GenerateWorkDirConfig generates a config with default values and uses db from current working directory.
func (*HypervisorConfig) EffectiveDeskAddr ¶
func (c *HypervisorConfig) EffectiveDeskAddr() string
EffectiveDeskAddr is where the desk is served: DeskAddr, or the platform default when the config predates the field or leaves it empty. Empty is the browser's default, and there it means no desk listener.
func (*HypervisorConfig) FillDefaults ¶
func (c *HypervisorConfig) FillDefaults()
FillDefaults fills the config with default values.
func (*HypervisorConfig) Parse ¶
func (c *HypervisorConfig) Parse(path string) error
Parse parses the file at path and decodes its JSON into c.
Tagged !js because it pulls encoding/json (and via that, the reflect runtime helpers TinyGo's stdlib doesn't provide). Browsers don't have filesystem access anyway, so the method has no use under js/wasm — callers in that context should construct HypervisorConfig in memory.
type Key ¶
type Key []byte
Key allows a byte slice to be marshaled or unmarshaled from a hex string.
func (Key) MarshalText ¶
MarshalText implements encoding.TextMarshaler
func (*Key) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler
type KeyEntry ¶
type KeyEntry struct {
Index uint32 `json:"index"`
Label string `json:"label,omitempty"`
PublicKey string `json:"public_key"`
SecretKey string `json:"secret_key"`
}
KeyEntry is one deterministically-derived keypair in the visor KeyRing — the derivation index, a purpose label, and the public/secret key. (No skycoin address: the keyring's only use is the standalone-hypervisor identity keypair, not a payment wallet.)
type KeyRing ¶
type KeyRing struct {
Type string `json:"type"`
NextIndex uint32 `json:"next_index"`
Entries []KeyEntry `json:"entries,omitempty"`
}
KeyRing is a small wallet-like set of deterministically-derived keys recorded in the visor config, shaped loosely like a skycoin deterministic wallet (a type + tracked next-index + entries with public/secret keys).
The keys are derived ONE-WAY from the visor secret key (the implicit seed) via cipher.DeriveChildKey, so they are regenerable from the one visor key (no separate backup) and a leaked child can't expose the visor key. This is used to mint standalone-hypervisor identities (the since-retired cli hv gen) deterministically instead of random, unrelated keys — so the standalone hypervisor is provably a child of the visor and recoverable.
type LANDmsgServerConf ¶
type LANDmsgServerConf struct {
Enable bool `json:"enable"` // Enable the LAN/WAN DMSG server.
Port int `json:"port,omitempty"` // TCP port (0 = auto-select; auto-selected port changes on every restart, so set explicitly for stable address).
PublicAddress string `json:"public_address,omitempty"` // Operator-set "host:port" advertised to remote visors (e.g. "203.0.113.5:8082"). Empty = LAN-only.
MaxSessions int `json:"max_sessions,omitempty"` // Max concurrent sessions (default 100).
PK cipher.PubKey `json:"pk,omitempty"` // Server public key (auto-generated if empty).
SK cipher.SecKey `json:"sk,omitempty"` // Server secret key (auto-generated if empty).
PublicDiscoveryURL string `json:"public_discovery_url,omitempty"` // Override for the dmsg-discovery URL advertised to managed visors (e.g. "http://hypervisor.example.com:8000"). Empty = auto-derived from HTTPAddr when remotely reachable.
}
LANDmsgServerConf configures an embedded DMSG server for LAN visors, optionally reachable from outside the LAN as well. When enabled, the hypervisor acts as a local DMSG relay so its managed visors don't need to route through public DMSG servers. With Port pinned and PublicAddress set, remote visors can also reach the server (operator must configure port forwarding on the router for the chosen Port). The server keypair is stored in the config so it persists across restarts.
type Launcher ¶
type Launcher struct {
ServiceDisc string `json:"service_discovery"`
ServiceDiscDmsg string `json:"service_discovery_dmsg,omitempty"` // DMSG-HTTP URL for service discovery
// Apps uses the appsList wrapper so on-disk JSON renders each
// AppConfig's Args as a single shell-like string (much easier
// to hand-edit than the array form). Old configs with array-
// form args still load. Behaviorally this is a []appserver.AppConfig
// — readers that take the Apps field unchanged still work.
Apps appsList `json:"apps"`
ServerAddr string `json:"server_addr"`
BinPath string `json:"bin_path"`
DisplayNodeIP bool `json:"display_node_ip"`
HeartbeatInterval Duration `json:"heartbeat_interval,omitempty"`
}
Launcher configures the app
type LogServer ¶
type LogServer struct {
// LocalAddr enables serving on localhost (e.g., "localhost:8002" or "127.0.0.1:8002").
// If empty, localhost serving is disabled (dmsg-only mode).
LocalAddr string `json:"local_addr"`
}
LogServer configures the dmsghttp log server's optional localhost endpoint. When LocalAddr is set (non-empty), the log server also serves on localhost without authentication.
type LogStore ¶
type LogStore struct {
// Type defines the log store type. Valid values: file, memory.
Type string `json:"type"`
Location string `json:"location"`
RotationInterval Duration `json:"rotation_interval"` // time value, examples: 10s, 1m, 1h etc
}
LogStore configures a LogStore.
type NetworkPolicy ¶
type NetworkPolicy struct {
Allow []string `json:"allow,omitempty"`
Deny []string `json:"deny,omitempty"`
// Metered is the mode used while the network is metered: normal, lean,
// dmsg-only or offline. Empty means lean.
Metered string `json:"metered,omitempty"`
// MeteredInterfaces are interface globs or address ranges. The network
// counts as metered while the route to the internet leaves through one.
MeteredInterfaces []string `json:"metered_interfaces,omitempty"`
}
NetworkPolicy is transport.network: which interfaces and addresses the visor offers to peers. Entries are interface name globs or address ranges, deny wins, and an empty allow list allows everything.
func (NetworkPolicy) IsZero ¶
func (p NetworkPolicy) IsZero() bool
IsZero reports whether the policy is the default one.
type Pty ¶
type Pty struct {
DmsgPort uint16 `json:"dmsg_port"`
CLINet string `json:"cli_network"`
CLIAddr string `json:"cli_address"`
Whitelist []cipher.PubKey `json:"whitelist"`
// SshListen, when non-empty, brings up a direct-TCP entry point
// for the dmsgpty / dmsgscp / dmsgcat protocols, separate from the
// dmsg-overlay path. Listening address in net.Listen format —
// `:2022`, `0.0.0.0:2022`, `127.0.0.1:2022`, etc. Empty (default)
// disables it: only the dmsg-overlay entry point is served.
//
// This is the surface that `skywire cli pty shell` (client) and
// `skywire cli pty host` (server) expose as the OpenSSH-equivalent
// shell over skywire identity.
//
// Auth flow per accepted TCP connection:
// 1. Noise XK handshake using the visor's identity keypair —
// the client side pins the server's PK (same trust model as
// ssh's host key check) and the server learns the client's PK
// from the handshake.
// 2. The learned client PK is checked against the SAME whitelist
// that gates the dmsg-overlay accept loop. No separate ACL.
// 3. On accept, the stream is handed to the existing dmsgpty
// mux so dmsgpty / dmsgscp / dmsgcat clients work uniformly
// over either transport.
//
// Motivation: operators who have direct IP reachability to a peer
// can `cli pty shell <pk>@<host>:<port>` — no dmsg-discovery dependency,
// lower latency for known endpoints, same PK-based auth model.
SshListen string `json:"ssh_listen,omitempty"`
// PersistentSessions, when true, keeps an interactive pty's shell
// running across a dropped stream instead of killing it: the host
// detaches the follower and a reconnecting client (the hypervisor web
// terminal, or `cli pty`) reattaches by session id, replaying buffered
// output. A bounded GC reaps detached sessions after an idle TTL so an
// abandoned shell can't leak. Off by default — opt in per visor; the
// web terminal degrades to fresh-shell-on-reconnect against hosts that
// have it off.
PersistentSessions bool `json:"persistent_sessions,omitempty"`
// AllowRPCExec gates the visor-RPC-initiated dmsgpty exec path
// (API.DmsgPtyExec, reached by `skywire cli dmsg pty` / `cli pty
// exec --via`). OFF by default: without it, any local process that
// can reach the visor's RPC socket (loopback :3435 on a root
// deployment — every local user) could borrow the visor's identity
// to open an authenticated dmsgpty stream to itself or to any peer
// that whitelists this visor, i.e. a local privilege-escalation /
// lateral-movement vector distinct from the whitelist-gated INBOUND
// dmsgpty host. Enabling it is an operator opt-in for a control /
// jump node that legitimately drives remote `cli pty exec` sessions.
// This gate does NOT affect the inbound dmsgpty host (the hypervisor
// web terminal and standalone dmsgpty-cli use separate, whitelist-
// gated paths), only the outbound RPC-initiated exec.
AllowRPCExec bool `json:"allow_rpc_exec,omitempty"`
}
Pty configures the pseudoterminal subsystem (formerly Dmsgpty). The struct shape is unchanged across the rename; the V1.Pty field's JSON tag accepts both "pty" (canonical) and "dmsgpty" (legacy) via V1.UnmarshalJSON in config_compat.go.
type PublicVisorConfig ¶
type PublicVisorConfig struct {
// RegistrationTimeout is how long to wait for an external STCPR connection
// before deregistering from service discovery. This validates the visor is
// actually reachable from the internet (has port forwarding configured).
// Set to 0 to skip this check (stay registered regardless).
// Default: 10m
RegistrationTimeout Duration `json:"registration_timeout,omitempty"`
// MaxTransports is the DISTINCT-PEER count at which a public visor
// drain-pauses its service-discovery registration, resuming when the
// count drops back below it. It counts unique remote visors, not raw
// transports — several transport types to the same peer count as one.
// Set to 0 to never drain based on peer count.
// Default: PublicVisorMaxTransports (2048).
MaxTransports int `json:"max_transports,omitempty"`
}
PublicVisorConfig configures public visor behavior and service discovery registration.
type ResolverConfig ¶
type ResolverConfig struct {
// Name identifies this resolver in logs and as the launcher app row
// ("resolver-<name>", so `cli visor app start resolver-lan` works). It
// must be unique among resolvers and is restricted to [a-z0-9-] so it
// stays a usable app name. Empty gets "r<index>" assigned at load.
Name string `json:"name,omitempty"`
// Kind is "dmsg" (default) or "skynet" — which transport the resolver
// dials destinations over. See the ResolverKind* constants.
Kind string `json:"kind,omitempty"`
// Enable must be true for this resolver to bind its listener at boot.
// A disabled entry is still constructed, so it can be started later
// through the launcher, and is excluded from port-conflict checking
// (it binds nothing).
Enable bool `json:"enable"`
// SecretKey runs a dmsg resolver under its OWN dmsg identity instead of
// the visor's, attached in-process to the visor's relay — see
// DmsgWebConfig.SecretKey for the full rationale and for why this is not
// the second-full-client pattern that got removed in #4500/#4501.
//
// Meaningless for a skynet resolver, which reaches destinations through
// the visor's router and therefore always speaks as the visor; setting it
// on one is rejected rather than ignored.
SecretKey *cipher.SecKey `json:"secret_key,omitempty"`
// ProxyPort is this resolver's SOCKS5 listener port. REQUIRED and
// non-zero: unlike the primaries there is no sensible default to fall
// back to, and defaulting would silently land a second resolver on 4445
// on top of dmsg_web.
ProxyPort uint `json:"proxy_port"`
// ProxyAddr is the host the listener binds. Empty = loopback
// ("127.0.0.1"). "0.0.0.0" or a LAN IP serves other devices — only do
// that on a trusted network, and prefer giving such a resolver its own
// SecretKey so the LAN's requests are attributable to a key you can
// revoke without rotating the visor's.
ProxyAddr string `json:"proxy_addr,omitempty"`
// DomainSuffix is the TLD this resolver treats as mesh addresses.
// Empty uses the kind's default (".dmsg" / ".skynet"). A second resolver
// may serve the SAME suffix as the primary — that is the LAN-gateway
// case, two listeners answering for .dmsg under different identities.
DomainSuffix string `json:"domain_suffix,omitempty"`
// UpstreamSOCKS forwards CONNECTs that don't match DomainSuffix to this
// SOCKS5 server. Set explicitly it always wins; left empty its value
// depends on Chain.
UpstreamSOCKS string `json:"upstream_socks,omitempty"`
// UpstreamRules send chosen domains to another upstream (a second
// skysocks-client, say) or "direct", ahead of UpstreamSOCKS. The longest
// matching suffix wins.
UpstreamRules []proxyroute.Rule `json:"upstream_rules,omitempty"`
// Chain, when nil or true (the default), gives an empty UpstreamSOCKS the
// same auto-chain the matching primary gets: a dmsg resolver chains to the
// skynet_web listener (so one browser proxy entry covers .dmsg and .skynet),
// and a skynet resolver chains to skysocks-client (so clearnet exits over
// the mesh). Set false to leave the resolver unchained — non-matching
// CONNECTs then leave this host directly, which is what a LAN .dmsg gateway
// that must NOT become an open clearnet proxy wants.
Chain *bool `json:"chain,omitempty"`
// Alias is the friendly hostname label for THIS visor's own PK under this
// resolver's suffix. Defaults to "skywire".
Alias string `json:"alias,omitempty"`
// SelfLoopback / SelfLoopbackAuthenticated mirror the identically-named
// DmsgWebConfig fields; nil means on.
SelfLoopback *bool `json:"self_loopback,omitempty"`
SelfLoopbackAuthenticated *bool `json:"self_loopback_authenticated,omitempty"`
// RouteTimeout is the keepalive for routes a SKYNET resolver creates.
// Zero uses the router default. Meaningless for a dmsg resolver.
RouteTimeout Duration `json:"route_timeout,omitempty"`
// TLSMITM / TLSPort / TLSUpstreamPort / TLSCAPath / TLSCAKeyPath mirror the primaries' TLS
// interception knobs; see DmsgWebConfig for the trust model. Off by
// default, and a CA that fails to load downgrades to plain HTTP with a
// warning rather than failing the resolver.
TLSMITM bool `json:"tls_mitm,omitempty"`
TLSPort uint16 `json:"tls_port,omitempty"`
TLSUpstreamPort uint16 `json:"tls_upstream_port,omitempty"`
TLSCAPath string `json:"tls_ca_path,omitempty"`
TLSCAKeyPath string `json:"tls_ca_key_path,omitempty"`
}
ResolverConfig is ONE additional resolving proxy: its own SOCKS5 listener, its own domain suffix, and — for a dmsg resolver — optionally its own dmsg identity.
The point of more than one is that a resolver's listener is also its access policy. A loopback resolver on 4445 serves this host; a second one bound to 0.0.0.0 on another port with a DIFFERENT secret key serves the LAN under an identity the operator can whitelist (or revoke) separately, without the LAN's traffic ever appearing to come from the visor's own key. Before this existed the only way to get that was a second visor.
func ParseResolverSpec ¶
func ParseResolverSpec(spec string) (ResolverConfig, error)
ParseResolverSpec parses ONE entry of the RESOLVERS bash array / the --resolvers CSV into a ResolverConfig.
Grammar, chosen so a whole resolver fits in one shell-array element and a comma can still separate entries:
<kind>:<port>[;key=value]...
e.g.
dmsg:4447 dmsg:4447;addr=0.0.0.0;name=lan;sk=<64-hex>;chain=false skynet:4448;suffix=.skynet;upstream=127.0.0.1:1080
Recognized keys: name, addr, suffix, sk, upstream, chain, alias. An unrecognized key is an ERROR, not a shrug — a typo'd "adrr=0.0.0.0" that parsed silently would hand the operator a loopback-only resolver they believe is serving the LAN. Everything else (TLS interception, route timeouts, self-loopback) stays JSON-only: those are set once by hand, not from a .conf one-liner.
func ParseResolverSpecs ¶
func ParseResolverSpecs(csv string) ([]ResolverConfig, error)
ParseResolverSpecs parses a comma-separated list of specs — the shape cmdutil.SkyenvArray hands back for RESOLVERS=('…' '…') — assigning each unnamed entry a stable default name.
func (*ResolverConfig) AppName ¶
func (r *ResolverConfig) AppName() string
AppName is the launcher app row this resolver appears as. Prefixed so a resolver can never shadow a built-in app's registration — a resolver named "vpn-client" would otherwise replace the VPN client's entry in the global app registry, and the visor would launch a SOCKS5 proxy where the operator asked for a VPN.
func (*ResolverConfig) Chained ¶
func (r *ResolverConfig) Chained() bool
Chained reports whether an unset UpstreamSOCKS should inherit the matching primary's auto-chain. Nil (the common case, and every entry written before the knob existed) means yes.
func (*ResolverConfig) IsSkynet ¶
func (r *ResolverConfig) IsSkynet() bool
IsSkynet reports whether this entry resolves over skywire routing.
func (*ResolverConfig) ToDmsgWeb ¶
func (r *ResolverConfig) ToDmsgWeb() *DmsgWebConfig
ToDmsgWeb projects this entry onto the DmsgWebConfig the EmbeddedDmsgWeb runtime consumes. The returned config is a fresh value the runtime owns: SetUpstream/SetBind mutate it, and those runtime-only changes deliberately do not write back into V1.Resolvers (which is the on-disk shape).
func (*ResolverConfig) ToSkynetWeb ¶
func (r *ResolverConfig) ToSkynetWeb() *SkynetWebConfig
ToSkynetWeb is ToDmsgWeb's counterpart for a skynet entry. SecretKey is dropped on purpose — see the field comment.
type RewardsConfig ¶
type RewardsConfig struct {
Enable bool `json:"enable"`
WorkDir string `json:"work_dir"`
Whitelist []cipher.PubKey `json:"whitelist,omitempty"`
CanonicalDomain string `json:"canonical_domain,omitempty"`
SkycoinNode string `json:"skycoin_node,omitempty"`
LoginNode string `json:"login_node,omitempty"`
}
RewardsConfig configures the reward system UI when hosted by the visor.
type Routing ¶
type Routing struct {
RouteSetupNodes []cipher.PubKey `json:"route_setup_nodes,omitempty"`
UserRouteSetupNodes []cipher.PubKey `json:"user_route_setup_nodes,omitempty"` // user-added keys, preserved across config refresh
RouteSetupSK *cipher.SecKey `json:"route_setup_sk,omitempty"` // Embedded route setup-node secret key
RouteFinder string `json:"route_finder"`
RouteFinderDmsg string `json:"route_finder_dmsg,omitempty"`
RouteFinderTimeout Duration `json:"route_finder_timeout,omitempty"`
MinHops uint16 `json:"min_hops"`
// NoTransit, when true, makes this visor refuse to be an INTERMEDIATE hop
// on anyone else's route. It still originates its own routes and still
// accepts routes that END here, and it is unaffected by anything that does
// not install a routing rule — dmsg-over-skynet relaying rides VStreamMux
// at route ID 0 and is deliberately not a route, so a visor hosting a dmsg
// server can carry that traffic while refusing transit.
//
// This is the ENFORCING half and needs nobody's cooperation. The route
// finder is told separately so it stops proposing routes that would be
// refused here; a stale or missing advertisement costs a failed setup and
// a retry, never a transited visor.
NoTransit bool `json:"no_transit,omitempty"`
// CalculateRoutes enables local route calculation instead of using the route finder service.
// When enabled, routes are calculated locally using cached TPD data.
// Can be overridden at runtime with --use-rf flag.
CalculateRoutes bool `json:"calculate_routes,omitempty"`
// MuxRoutes is the number of parallel routes to establish per connection.
// 0 or 1 = single route (default), >1 = route multiplexing across transports.
// ParallelRouteSetup is the number of candidate route groups whose setup
// DialRoutes races concurrently per attempt (first to complete its
// handshake wins). 0 (unset) uses the built-in default (a small N); 1
// forces the legacy strictly-sequential setup (clean rollback switch).
// This is the steady-connection fix: it stops a run of dead candidates
// from burning the dial ceiling and wedging an app in "starting".
ParallelRouteSetup int `json:"parallel_route_setup,omitempty"`
// ExcludeSameLanHops, when nil or true (the default), makes route
// calculation drop candidate INTERMEDIATE hops that sit on this visor's own
// local network — a peer reached over a private/RFC1918 endpoint, or (the
// NAT-hairpin case) a peer reached at this visor's own public IP / its /24.
// Routing a remote-destination path through a same-LAN peer adds a hop but
// no path diversity (same first-mile link, NAT and failure domain) and
// starves a mux of genuinely-diverse aux legs (the same-LAN peer wins on
// latency and is re-picked every rotation). Same-LAN peers remain usable as
// direct dial DESTINATIONS; only their use as intermediates is suppressed.
// Set to false only for deliberate LAN-relay topologies. Pointer so an
// absent field (older configs) defaults ON.
ExcludeSameLanHops *bool `json:"exclude_same_lan_hops,omitempty"`
// TransportPreference is the transport-type priority order, most-preferred
// first. It decides both which existing transport a route rides when
// several reach the same peer, and which type the visor tries to CREATE
// first when a route needs a transport that isn't there yet — so the first
// entry is the primary transport and the rest are the fallbacks, in order.
// Valid values are the names in tptypes.Known() ("stcpr", "squicr",
// "sudph", "stcp", "webrtc", "swsr", "swtr", "dmsg"; the pre-rename
// "quic"/"ws"/"wt" are accepted too). If empty, the built-in default order
// (stcpr > squicr > sudph > stcp > webrtc > swsr > swtr > dmsg) is used.
// Types not listed here sort last. Settable at runtime, without a restart,
// via PUT /api/visors/{pk}/router-settings.
TransportPreference []string `json:"transport_preference,omitempty"`
// RouteExcludeTransportTypes hard-EXCLUDES routes that traverse any of these
// transport types, rather than merely deprioritizing them like
// TransportPreference does. A route (in either direction) is dropped from the
// candidate set if ANY of its hops rides an excluded type — so an excluded type
// never enters the mux leg pool. Intended for types whose flakiness poisons the
// mux (e.g. "webrtc": a stalled webrtc leg wedges the no-skip reorder frontier
// and truncates downloads). Empty by default (nothing excluded), so it stays
// opt-in — a visor that NEEDS a type (e.g. webrtc for a wasm/browser peer) simply
// leaves it off its own list. Values use the same names as TransportPreference.
RouteExcludeTransportTypes []string `json:"route_exclude_transport_types,omitempty"`
// MuxFEC opts INTO forward error correction on mux route groups: repair
// frames (K=8, R=2) are striped alongside data so a lost frame can be
// rebuilt without a retransmit. OFF by default: the legs ride reliable
// transports and SACK recovery already refills a gap, so the repair
// traffic was a flat 25%+ wire overhead on every multi-leg group (measured
// live: 5 MB of repair for 6 MB of payload on one leg). Both ends must
// enable it for a group to negotiate it.
MuxFEC bool `json:"mux_fec,omitempty"`
// RouterSettings is the visor-wide router knob catalog: one map of
// catalog-name to formatted value, holding only the knobs an operator has
// explicitly SET (`skywire cli route settings k=v`). A knob absent from the
// map runs the value the binary compiled with, so the file stays small and a
// new release's changed default is picked up rather than pinned. Restored at
// boot by init_router, which is what makes a sweep survive a restart — the
// 17 router atomics used to reset.
//
// It is one map rather than a field per knob on purpose: the catalog grows
// every time a constant becomes live, and a field per knob would mean a
// config migration each time.
RouterSettings map[string]string `json:"router_settings,omitempty"`
// RouterAppSettings holds the same shape per APP name: the overrides
// `route settings --app <name> k=v` installs for the route groups that app
// owns, so a subject client and its paired reference keep their different
// values across a restart.
RouterAppSettings map[string]map[string]string `json:"router_app_settings,omitempty"`
// EnableCascadeRouteSetup opts INTO the source-driven cascade route-setup
// path (RSN signs, source injects the cascade down its own transports,
// avoiding the RSN's dmsg dependency). It is OFF by default: the cascade
// currently has a multihop data-plane bug (control plane succeeds on every
// hop, but the set-up route does not carry traffic), so the legacy path —
// where the setup node dials each hop directly over dmsg to install rules —
// is the default until that is fixed. Flip this to true (or change the
// default) once the cascade data plane is corrected.
EnableCascadeRouteSetup bool `json:"enable_cascade_route_setup,omitempty"`
// PolicyPerDial is an optional operator-supplied routing
// policy. Accepts either:
// "preset:<name>" — a curated policy embedded in the
// binary, no file or compile needed
// (e.g. "preset:spread-bw"); presets
// live in pkg/router/policy/presets
// "@/path/to/policy.star" — Starlark, file-backed, hot-reloaded
// "@/path/to/policy.wasm" — WASM, file-backed, hot-reloaded
// "<inline script source>" — small inline Starlark policies
// "" / unset — no policy, built-in defaults
//
// The visor picks the backend by file extension when the
// value starts with "@" (`.wasm` → WASM, anything else →
// Starlark). Inline values are always Starlark — compiled
// WASM doesn't fit in a JSON string. Both backends produce
// the same RouteSpec per dial and route through the same
// router.DialHook.
//
// See docs/routing_policy_rfc.md for the Starlark DSL,
// docs/examples/routing-policies/wasm/README.md for the
// WASM ABI + trade-offs, and `skywire cli route policy
// test/bench` for iteration tools.
PolicyPerDial string `json:"policy_per_dial,omitempty"`
// PolicyOnControlPorts opts the routing policy (PolicyPerDial / per-app)
// BACK IN for control-plane destination ports (pty 22, dmsgctrl 7, setup
// 36, hypervisor RPC 46, transport-setup 47/48, …). Default false: those
// small noise-XK control channels bypass the policy and take a plain base
// route, because a bandwidth-spreading policy (e.g. the adaptive default)
// destabilizes their handshakes — the pty 32-leg-mux footgun. Set true
// only with a policy purpose-built for control traffic.
PolicyOnControlPorts bool `json:"policy_on_control_ports,omitempty"`
}
Routing configures routing.
type Services ¶
type Services = deployment.Services
Services is subdomains and IP addresses of the skywire services as deployed. Aliased to deployment.Services — both packages used to declare an identical struct, which created two json.Unmarshal hot paths (one in deployment.init, one in visorconfig.MakeBaseConfig) for the same data shape. The alias collapses the duplication.
type Skychat ¶
type Skychat struct {
// GroupHistoryDB, when non-empty, enables persistent group-message
// history at the visor layer. Each inbound group message is mirrored
// into a bolt store at this path. The Visor.GroupHistory RPC (and
// `skywire cli skychat group history`) read from the same store.
//
// Path may be absolute (used verbatim) or relative — relative paths
// resolve under <LocalPath>/skychat/. Empty (default) disables
// persistence: groups remain in-memory-only, bounded by the inbox
// ring buffer (~1024 messages).
//
// Limits track history.DefaultLimits() with a couple of group-
// aware overrides (rate-limit disabled, larger per-group cap).
// A future config knob may expose the limits directly.
GroupHistoryDB string `json:"group_history_db,omitempty"`
}
Skychat carries chat-app-related visor-side configuration. Optional — nil disables all knobs (the visor still runs chat groups in-memory).
type SkymailBridgeConfig ¶
type SkymailBridgeConfig struct {
// Enable must be true for the bridge to start.
Enable bool `json:"enable"`
// Addr is the local TCP listener Postfix's transport_map relays
// to (e.g. "127.0.0.1:1025"). Default "127.0.0.1:1025".
Addr string `json:"addr,omitempty"`
// Mode selects the RCPT TO rewrite rule. Both modes accept both
// address shapes (host-prefixed and bare-PK); they differ only
// in what mode b does with the host-prefixed shape.
//
// "b" (default) — strip-when-prefixed: ".<pk><suffix>" is
// stripped from host-prefixed envelopes before
// forwarding (so the receiver's existing
// virtual_alias chain handles delivery
// natively); bare-PK envelopes are forwarded
// verbatim. Strict superset of mode a.
// "a" — verbatim: forward both shapes unchanged.
// Receiver's Postfix must accept the full
// ".<pk><suffix>" domain in every envelope.
//
// Either way, support for the bare-PK shape requires the
// receiver's Postfix to know the bare-PK domain — via
// mydestination or virtual_alias_domains.
Mode string `json:"mode,omitempty"`
// Suffix is the TLD the bridge treats as a skywire-routed
// recipient. Defaults to ".skynet". Independent of the resolver's
// DomainSuffix — operators may serve mail under a different TLD
// than the resolver if they have reason to.
Suffix string `json:"suffix,omitempty"`
// HeloName is the EHLO/HELO greeting the bridge sends to the
// peer's Postfix. Mostly cosmetic (appears in Received: trace
// headers). Default "skymail-bridge.local".
HeloName string `json:"helo_name,omitempty"`
// RemotePort is the dmsg routing port to dial on the peer.
// Default 25 (matching the SMTP convention). Receiver must
// expose Postfix's smtpd on this port via
// `skywire cli serve add 25 --to 127.0.0.1:25`.
RemotePort uint16 `json:"remote_port,omitempty"`
}
SkymailBridgeConfig configures the embedded SMTP-aware bridge that relays recipient envelopes of either shape — user@<host>.<base32-pk>.skynet or user@<base32-pk>.skynet — to a peer visor's exposed SMTP listener over the visor's existing dmsg session.
Structurally parallel to DmsgWebConfig — runs inside the visor process, lifecycled via RPC (SetEmbeddedProxyEnabled("bridge", …)), no separate launcher app needed. Operators who don't run a full visor can use the standalone binary at cmd/smb instead.
type SkymailConfig ¶
type SkymailConfig struct {
// Enable starts the mailbox.
Enable bool `json:"enable"`
// Dir is the Maildir root. Default: "mail" beside the local path
// (outside it, because the wasm visor never persists local/).
Dir string `json:"dir,omitempty"`
// MaxMessageSize, MaxTotalSize (bytes, Inbox and Sent together) and
// MaxAge bound what the mailbox keeps: 0 means the default (10 MiB,
// 64 MiB, 7 days), a negative value no bound.
MaxMessageSize int64 `json:"max_message_size,omitempty"`
MaxTotalSize int64 `json:"max_total_size,omitempty"`
MaxAge Duration `json:"max_age,omitempty"`
}
SkymailConfig configures the visor's own mailbox (pkg/skymail): mail to <anything>@<base32-pk>.skynet or .dmsg, received over SMTP on port 25 and kept in a Maildir. The section is optional: absent, the mailbox runs with the default limits. A visor whose port 25 is forwarded to Postfix (`serve add 25`) leaves it to Postfix.
type SkynetWebConfig ¶
type SkynetWebConfig struct {
// Enable must be true for the resolver to start.
Enable bool `json:"enable"`
// ProxyPort is the local SOCKS5 listener. Default 4446.
ProxyPort uint `json:"proxy_port,omitempty"`
// ProxyAddr is the host the SOCKS5 proxy binds to. Empty = loopback
// ("127.0.0.1"). Set to "0.0.0.0" or a LAN IP to serve other devices on
// the network (a `.skynet` gateway for a home router's LAN). Only bind a
// non-loopback address on a trusted network.
ProxyAddr string `json:"proxy_addr,omitempty"`
// WebPort is the local HTTP bridge port. Default 8081.
WebPort uint `json:"web_port,omitempty"`
// DomainSuffix is the TLD treated as skynet addresses. Default ".skynet".
DomainSuffix string `json:"domain_suffix,omitempty"`
// UpstreamSOCKS forwards non-matching CONNECTs to this upstream.
UpstreamSOCKS string `json:"upstream_socks,omitempty"`
// UpstreamRules send chosen domains to another upstream (a second
// skysocks-client, say) or "direct", ahead of UpstreamSOCKS. The longest
// matching suffix wins.
UpstreamRules []proxyroute.Rule `json:"upstream_rules,omitempty"`
// RouteTimeout is the keepalive duration for routes created by the
// resolving proxy. Routes idle longer than this are expired by GC.
// Zero means use DefaultRouteKeepAlive (10 min). A very large value
// (e.g. "8760h") effectively keeps routes alive until the visor stops.
RouteTimeout Duration `json:"route_timeout,omitempty"`
// TLSMITM enables on-the-fly TLS termination for browser
// connections to <pk>.skynet on TLSPort, using a leaf cert minted
// from a locally-installed CA at TLSCAPath/TLSCAKeyPath. Off by
// default; when off the resolver behaves exactly as before.
// See pkg/skynetca and docs/skynet-tls.md for the trust model.
TLSMITM bool `json:"tls_mitm,omitempty"`
// TLSPort is the destination port treated as TLS. Default 443.
TLSPort uint16 `json:"tls_port,omitempty"`
// TLSUpstreamPort is the destination port a MITM'd connection is sent
// to once decrypted. Default 80, where visors serve HTTP.
TLSUpstreamPort uint16 `json:"tls_upstream_port,omitempty"`
// TLSCAPath / TLSCAKeyPath point to a CA generated by
// `skywire cli resolver ca gen`. Both required when TLSMITM
// is true. The visor logs and falls back to non-MITM mode if
// either is missing or unreadable; it does not refuse to start.
TLSCAPath string `json:"tls_ca_path,omitempty"`
TLSCAKeyPath string `json:"tls_ca_key_path,omitempty"`
// SelfLoopback, when nil or true (the default), serves a request whose
// destination is THIS visor's own PK from the local service in-process
// (net.Pipe) instead of dialing a skynet route back to self. Set false
// to exercise the full self-route path (valid for skynet, useful for
// testing self-transports).
SelfLoopback *bool `json:"self_loopback,omitempty"`
// Alias is a friendly hostname label for THIS visor's own PK, so
// "<alias>.skynet" resolves to the local visor without hardcoding its
// public key. Defaults to "skywire" when unset (→ "skywire.skynet").
Alias string `json:"alias,omitempty"`
// SelfLoopbackAuthenticated, when nil or true (the default), makes a
// self-loopback request (skywire.skynet / <self-pk>.skynet served
// in-process via SelfDial) present THIS visor's own PK as the
// authenticated caller, so the in-process self view renders the same
// authenticated landing-page endpoints a survey-whitelisted remote peer
// sees. Set false to render the self-loopback landing page anonymously.
SelfLoopbackAuthenticated *bool `json:"self_loopback_authenticated,omitempty"`
}
SkynetWebConfig enables the embedded `.skynet` resolving proxy — the skywire-routing counterpart to DmsgWebConfig. Browsers pointed at the SOCKS5 listener can load http://<pk>.skynet[:<port>] URLs, which the visor serves by dialing a route to the remote visor's skynet server and piping bytes through. Reuses the visor's own router, so no additional routing setup is needed.
Ports default to non-conflicting values with DmsgWebConfig so both resolvers can run simultaneously on a single visor.
type Stats ¶
type Stats struct {
// Path is the absolute path to the bbolt file. Empty = "<local_path>/stats.db".
Path string `json:"path,omitempty"`
// SampleInterval is the sampler tick (e.g. "1m"). Empty = 1m.
SampleInterval Duration `json:"sample_interval,omitempty"`
// RetentionDays caps bbolt history. 0 = 30 days.
RetentionDays int `json:"retention_days,omitempty"`
// CXOPublishWindow caps the rolling window the CXO publisher
// exposes (in days). 0 = 7. Must be ≤ RetentionDays.
CXOPublishWindow int `json:"cxo_publish_window,omitempty"`
// Disabled, when true, skips the entire telemetry store and
// associated /stats/* endpoints + CXO publisher.
Disabled bool `json:"disabled,omitempty"`
// DedicatedTPListFeed, when true, publishes the transport-list
// discovery snapshot on its OWN CXO node/port
// (skyenv.DmsgVisorTPListCXOPort) instead of on the telemetry feed.
//
// Default (false) is the CONSOLIDATED single-feed model: the tp-list
// leaf rides the telemetry Root as a top-level leaf, so TPD holds ONE
// CXO connection per visor instead of two. TPD's aggregator extracts
// the tp-list via its targeted discovery-leaf fetch, which lands the
// small leaf reliably now that the compact sharded telemetry keeps the
// whole Root small (≈16 shard leaves + 1 tp-list leaf) — the condition
// that was missing when the earlier consolidation (#4184) had to be
// reverted (#4189) over a bulky per-transport-leaf Root. Halving the
// visor↔TPD connections cuts TPD's per-connection goroutines and the
// per-session dmsg handshake load on the dmsg servers.
//
// Set true only as a transitional escape hatch — e.g. if a busy hub's
// tp-list is ever observed to under-fill on the combined feed.
DedicatedTPListFeed bool `json:"dedicated_tplist_feed,omitempty"`
}
Stats configures the visor-local telemetry store. Defaults are applied lazily when the field is nil or any individual sub-field is its zero value, so dropping the entire block from a config is safe.
type Survey ¶
type Survey struct {
Timestamp time.Time `json:"timestamp"`
PubKey cipher.PubKey `json:"public_key,omitempty"`
SkycoinAddress string `json:"skycoin_address,omitempty"`
GOOS string `json:"go_os,omitempty"`
GOARCH string `json:"go_arch,omitempty"`
SYSINFO sysinfo.SysInfo `json:"zcalusic_sysinfo,omitempty"`
IPAddr string `json:"ip_address,omitempty"`
Disks *ghw.BlockInfo `json:"ghw_blockinfo,omitempty"`
Product *ghw.ProductInfo `json:"ghw_productinfo,omitempty"`
Memory *ghw.MemoryInfo `json:"ghw_memoryinfo,omitempty"`
UUID uuid.UUID `json:"uuid,omitempty"`
SkywireVersion string `json:"skywire_version,omitempty"`
ServicesURLs Services `json:"services,omitempty"`
DmsgServers []string `json:"dmsg_servers,omitempty"`
}
Survey system hardware survey struct
type TPSDmsgConfig ¶
type TPSDmsgConfig struct {
// MinSessions is the minimum number of dmsg server sessions.
// 0 means connect to ALL available servers (recommended for TPS).
MinSessions int `json:"min_sessions"`
// ServerType filters which dmsg servers to connect to: "official", "community", or "" for all.
ServerType string `json:"server_type"`
}
TPSDmsgConfig configures the embedded Transport Setup Node's dmsg client. If nil, defaults are used: MinSessions=0 (connect to all servers), ServerType="" (all types).
type TPVizConfig ¶
type TPVizConfig struct {
Enable bool `json:"enable"` // Whether to enable tp viz UI at /tp-viz/.
SurveyDir string `json:"survey_dir,omitempty"` // Directory containing visor surveys.
CacheMaxAge int `json:"cache_max_age,omitempty"` // Cache max age in minutes (default: 5).
}
TPVizConfig configures the embedded transport visualizer.
type Transport ¶
type Transport struct {
Discovery string `json:"discovery"`
DiscoveryDmsg string `json:"discovery_dmsg,omitempty"` // DMSG-HTTP URL for transport discovery (fallback pair with discovery)
AddressResolver string `json:"address_resolver"`
AddressResolverDmsg string `json:"address_resolver_dmsg,omitempty"` // DMSG-HTTP URL for address resolver
PublicAutoconnect bool `json:"public_autoconnect"`
// Autoconnect limits the transports public autoconnect dials. Unset, it
// keeps the built-in limits and two links per pair of sites for sudph.
Autoconnect Autoconnect `json:"autoconnect,omitzero"`
// HypervisorAutoconnect gates the background dialing of a direct (stcpr/sudph)
// transport to each configured hypervisor. A nil pointer means "on" (default),
// so configs written before this field existed keep the prior always-on
// behavior; set it to false to keep the hypervisor link on the dmsg relay only.
HypervisorAutoconnect *bool `json:"hypervisor_autoconnect,omitempty"`
// TCPNotSentLowat caps the unsent bytes the kernel holds for each stcpr or
// stcp transport (TCP_NOTSENT_LOWAT). Unset is 128 KiB, 0 is the kernel's
// default. Linux and Android only; it applies to transports opened after it.
TCPNotSentLowat *int `json:"tcp_notsent_lowat,omitempty"`
TransportSetupPKs []cipher.PubKey `json:"transport_setup"`
UserTransportSetupPKs []cipher.PubKey `json:"user_transport_setup,omitempty"` // user-added keys, preserved across config refresh
TPSetupSK *cipher.SecKey `json:"tps_sk,omitempty"`
TPSDmsg *TPSDmsgConfig `json:"tps_dmsg,omitempty"`
LogStore *LogStore `json:"log_store"`
StcprPort int `json:"stcpr_port"`
SudphPort int `json:"sudph_port"`
// TransportPort, when non-zero, is the single port number the transport types
// share: QUIC + sudph on <port>/udp and stcpr + WS on <port>/tcp, each socket
// demultiplexed by protocol (see docs/design/transport-port-unification.md),
// instead of binding a port each. 0 (default/omitted) keeps per-type binding.
// Per-type ports remain an override: a type whose own port (stcpr_port /
// sudph_port / quic_port) is non-zero breaks OUT onto that dedicated port even
// when transport_port is set; left 0 it rides the shared port. Opt-in while the
// unified-port path proves out.
TransportPort int `json:"transport_port,omitempty"`
// QUICPort optionally PINS the UDP port for the QUIC transport (#2607). QUIC
// is on by default (like stcpr/sudph); 0 (default/omitted) binds an ephemeral
// port — exactly as sudph_port does. Set a fixed port when a stable firewall
// rule is wanted; the bound port is registered with the address resolver
// either way, so a random port works and survives restarts.
QUICPort int `json:"quic_port,omitempty"`
// WTPort optionally PINS the UDP port for the WebTransport (WT) listener,
// mirroring quic_port. WT is HTTP/3-over-QUIC, so it needs its OWN UDP socket
// — it cannot share the unified transport_port with the raw-QUIC (squic)
// transport (two QUIC listeners can't bind one UDP port). 0 (default/omitted)
// binds an ephemeral port; the bound port + cert hash are registered with the
// address resolver either way. Set a fixed port to forward it through a NAT.
WTPort int `json:"wt_port,omitempty"`
// WSTable statically pins WebSocket (WS) transport peers by PK → wss:// URL —
// the WS analog of skywire-tcp's pk_table (STCP). It lets a visor DIAL a WS
// transport to a configured peer with NO address-resolver lookup (e.g. a peer
// on a LAN, or any visor whose wss endpoint you know), exactly as STCP pins
// TCP peers. Entries learned at runtime (autoconnect / a browser hook) are
// added on top; empty/omitted = no static WS peers.
WSTable map[cipher.PubKey]string `json:"ws_table,omitempty"`
// WTTable statically pins WebTransport (WT) peers by PK → {url, cert_hash} —
// the WT analog of ws_table. WT has no CA, so the dialer pins the peer's
// self-signed cert SHA-256 (cert_hash); url is the https:// endpoint. Lets a
// visor DIAL a WT transport to a configured peer with no address-resolver
// lookup. Runtime entries are added on top; empty/omitted = no static WT peers.
WTTable map[cipher.PubKey]WTPeer `json:"wt_table,omitempty"`
// QUICTable statically pins QUIC transport peers by PK → UDP address
// ("host:port") — the QUIC analog of skywire-tcp's pk_table (and of ws_table /
// wt_table). Lets a visor DIAL a QUIC transport to a configured peer with no
// address-resolver lookup. Consulted before the AR; empty/omitted = AR-only.
QUICTable map[cipher.PubKey]string `json:"quic_table,omitempty"`
// ARTransportLimit controls address resolver registration for privacy.
// Only its SIGN is meaningful — it is a static switch, not a counter:
// >= 0 (default): register with the address resolver
// < 0: never register with AR at all
// A positive value does NOT deregister after N transports. That was
// deliberately not implemented: a running visor must never drop its AR
// registration on a transport count, because AR discoverability is what
// lets peers dial it. Runtime load-shedding is a separate mechanism —
// the public-visor service-discovery drain (PublicVisorConfig.MaxTransports).
// When never-registered, the visor cannot receive new inbound transports
// but can still initiate outbound connections.
ARTransportLimit int `json:"ar_transport_limit,omitempty"`
// NoDirectTransports, when true, stops the visor from CREATING direct
// peer-to-peer transports (stcpr/sudph/stcp/squicr/swsr/swtr/webrtc) — whether
// via autoconnect, the on-demand route-setup hook, `tp add`, or `--direct`.
// DMSG (a relay through a dmsg server, not a direct link) is still created, so
// the visor keeps a baseline path and can still reach peers over existing
// transports/routes. Inbound transports peers dial to us are unaffected.
NoDirectTransports bool `json:"no_direct_transports,omitempty"`
// TransportCreateDeny is an advanced/testing per-type deny list of transport
// types the visor must not create, INCLUDING dmsg (to force a pure-direct or
// fully-offline visor). Applied on top of no_direct_transports. Legacy type
// aliases (quic/ws/wt) are accepted. Empty/omitted denies nothing.
TransportCreateDeny []string `json:"transport_create_deny,omitempty"`
// NetworkWatchInterval is how often the visor checks its interface
// addresses and route for a move to another network. Zero means 12s and
// a negative value turns the check off.
NetworkWatchInterval Duration `json:"network_watch_interval,omitempty"`
// PublicIPCheckInterval is how often the visor asks the dmsg servers
// for its public IP to notice a change the watcher cannot see. Zero
// means 12m and a negative value turns the check off.
PublicIPCheckInterval Duration `json:"public_ip_check_interval,omitempty"`
// Network limits the interfaces and addresses the visor advertises to
// peers. Nil advertises every non-virtual interface.
Network *NetworkPolicy `json:"network,omitempty"`
}
Transport defines a transport config.
type V1 ¶
type V1 struct {
*Common
Dmsg *dmsgspec.DmsgConfig `json:"dmsg"`
Pty *Pty `json:"pty,omitempty"`
Dmsgscp *Dmsgscp `json:"dmsgscp,omitempty"`
LogServer *LogServer `json:"log_server,omitempty"`
DmsgWeb *DmsgWebConfig `json:"dmsg_web,omitempty"`
SkynetWeb *SkynetWebConfig `json:"skynet_web,omitempty"`
Wisp *WispConfig `json:"wisp,omitempty"`
// HostBridge is the host bridge the visor uses for host sockets.
HostBridge *HostBridgeConfig `json:"host_bridge,omitempty"`
// Resolvers declares ADDITIONAL resolving proxies beyond the dmsg_web /
// skynet_web pair above — each with its own port, bind address, domain
// suffix and (for a dmsg one) its own identity. Purely additive: absent,
// which is every config written before it existed, means exactly the
// two primaries. See resolvers.go.
Resolvers []ResolverConfig `json:"resolvers,omitempty"`
BrowseOrigin *BrowseOriginConfig `json:"browse_origin,omitempty"`
SkymailBridge *SkymailBridgeConfig `json:"skymail_bridge,omitempty"`
Skymail *SkymailConfig `json:"skymail,omitempty"`
Rewards *RewardsConfig `json:"rewards,omitempty"`
STCP *tnspec.STCPConfig `json:"skywire-tcp,omitempty"`
Transport *Transport `json:"transport"`
Routing *Routing `json:"routing"`
Launcher *Launcher `json:"launcher"`
// EmbeddedServices are deployment services (transport-discovery,
// address-resolver, route-finder, service-discovery) this visor runs
// in-process, each mounted under a path prefix on its dmsg HTTP port
// and addressed as dmsg://<visor pk>:80/<prefix>/... Blocks have the
// same shape as a services.json entry plus an optional "prefix".
EmbeddedServices []services.Block `json:"embedded_services,omitempty"`
// DeploymentStatusAddr serves the status page of the embedded services
// over plain HTTP, such as 127.0.0.1:8082 for a reverse proxy. It is also
// at /status on the dmsg HTTP port. Empty serves it there only.
DeploymentStatusAddr string `json:"deployment_status_addr,omitempty"`
// Stats configures the visor-local telemetry store. Nil/zero
// values use defaults; Disabled=true skips the store entirely.
Stats *Stats `json:"stats,omitempty"`
// Skychat carries chat-app-related visor-side configuration that
// doesn't fit elsewhere. Currently just opt-in group-message
// persistence; expected to grow as the chat surface evolves.
Skychat *Skychat `json:"skychat,omitempty"`
// Wallet configures the skycoin-web wallet: whether to serve the
// frontend, and — the one axis the operator actually chooses — where
// keys live (browser | disk | remote). One block covers the native
// visor, `hv serve` and the wasm visor; what varies is which custody
// modes a given build can realize, which V1.WalletCustody() clamps.
// Nil (a config predating this block) serves the wallet with browser
// custody, i.e. existing behavior. See
// docs/design/skycoin-web-wallet-architecture.md.
Wallet *WalletConfig `json:"wallet,omitempty"`
// CoinNodes lists fibercoin nodes (see servicedisc.ServiceTypeCoin)
// this visor forwards over dmsg and advertises in service discovery,
// so a browser/thin-client wallet can discover and reach a node for
// the right coin over the mesh with no local full node and no launch
// flags. Each entry is health-gated (see pkg/visor/init_coinnode.go):
// the visor probes the node's HTTP /health on the SD heartbeat
// interval and only advertises it while it answers — no dead entries.
CoinNodes []CoinNodeConfig `json:"coin_nodes,omitempty"`
SurveyWhitelist []cipher.PubKey `json:"survey_whitelist"`
UserSurveyWhitelist []cipher.PubKey `json:"user_survey_whitelist,omitempty"` // user-added keys, preserved across config refresh
Hypervisors []cipher.PubKey `json:"hypervisors"`
CLIAddr string `json:"cli_addr"`
// KeyRing is a small wallet-like set of deterministically-derived keys (from
// the visor secret key). Currently mints standalone-hypervisor identities
// (the since-retired cli hv gen). Optional; absent on visors that never derived one.
KeyRing *KeyRing `json:"keyring,omitempty"`
LogLevel string `json:"log_level"`
LogHookLevel string `json:"log_hook_level,omitempty"` // most verbose level kept for /debug/log, info when empty
GCTraceSamples int `json:"gc_trace_samples,omitempty"` // samples kept for /debug/gctrace at 5 s each, 360 when zero
LocalPath string `json:"local_path"`
Skyenv string `json:"skyenv,omitempty"` // the skywire.conf config gen read; live changes are written back there
StunServers []string `json:"stun_servers"`
ShutdownTimeout Duration `json:"shutdown_timeout,omitempty"` // time value, examples: 10s, 1m, etc
IsPublic bool `json:"is_public"`
PublicVisorConfig *PublicVisorConfig `json:"public_visor,omitempty"`
GeoIP string `json:"geoip"`
PersistentTransports []tspec.PersistentTransports `json:"persistent_transports"`
ConfService string `json:"conf_service,omitempty"` // HTTP URL for config bootstrap service
ConfServiceDmsg string `json:"conf_service_dmsg,omitempty"` // DMSG URL for config bootstrap service
// SurveyClientSK is the CLI-side identity used by
// `skywire cli log <subcommand>` to authenticate against remote
// visors' survey_whitelist gates (visor logserver port 80 over
// dmsg). When non-empty, parses as a hex SecKey; the derived PK
// is the identity the remote checks against its survey_whitelist.
// Empty (default) falls through to --sk flag → DMSGCURL_SK env →
// ephemeral random keypair. Server-side: never read; the visor
// ignores this field entirely.
SurveyClientSK string `json:"survey_client_sk,omitempty"`
RewardAddress string `json:"reward_address,omitempty"`
RewardSystem string `json:"reward_system,omitempty"`
RewardSystemDmsg string `json:"reward_system_dmsg,omitempty"`
MemoryLimit string `json:"memory_limit,omitempty"` // Go memory limit: "none" (the default), a size like "256MiB", or "auto" for 90% of total RAM
// FlightRecorder keeps the last seconds of execution trace in memory for
// stall diagnosis. Off by default because it allocates heavily.
FlightRecorder bool `json:"flight_recorder,omitempty"`
// AppSettings holds the live tuning knobs an operator set per app
// (`skywire cli proxy settings`), so they survive a visor restart the way
// routing.mux_fec does. The visor restores them into the proc manager at
// boot and the app pulls them on its first tick; a `--reset` removes the
// entry. Absent on every config written before it existed, which means
// exactly "no knob was ever set".
AppSettings map[string]AppSettingsEntry `json:"app_settings,omitempty"`
Hypervisor *HypervisorConfig `json:"hypervisor,omitempty"`
// contains filtered or unexported fields
}
V1 is visor config
func MakeBaseConfig ¶
func MakeBaseConfig(common *Common, dmsgHTTP bool, services *Services, dmsgHTTPServersList *DmsgHTTPServers) *V1
MakeBaseConfig returns a visor config with 'enforced' fields only. This is used as default values if no config is given, or for missing *required* fields. This function always returns the latest config version.
func Parse ¶
func Parse(log *logging.Logger, r io.Reader, confPath string, visorBuildInfo *buildinfo.Info) (conf *V1, compat bool, err error)
Parse parses the visor config from a given reader. The config version is checked against the visor's version and if not the same we send back the error as well as compat(compatibility) as false.
func (*V1) AddAppConfig ¶
func (v1 *V1) AddAppConfig(launch *launcher.AppLauncher, appName, binaryName string) error
AddAppConfig add new config to apps if name was not same
func (*V1) ApplyDeploymentServices ¶ added in v1.3.99
ApplyDeploymentServices updates the deployment-managed fields of v1 from next, the deployment's current services config, and returns the JSON names of the fields it changed.
A service address or the STUN list is deployment-managed when the config holds nothing, the value prev held (the services config the visor last applied), or the embedded deployment default. Anything else was set by the operator and is left alone, so pointing one service at your own instance survives a refresh. An empty field is filled only when next differs from the embedded default, since empty already resolves to it.
The deployment key sets (route_setup_nodes, transport_setup, survey_whitelist) are replaced outright, as before: operators' own keys live in the user_* fields beside them and are merged at use time.
prev may be nil, meaning nothing has been applied yet.
func (*V1) AutoconnectSettings ¶
func (v1 *V1) AutoconnectSettings() Autoconnect
AutoconnectSettings returns a copy of the autoconnect limits.
func (*V1) DeleteAppArg ¶
func (v1 *V1) DeleteAppArg(launch *launcher.AppLauncher, appName string) error
DeleteAppArg Delete entire of args of a custom app
func (*V1) DeleteAppConfig ¶
func (v1 *V1) DeleteAppConfig(launch *launcher.AppLauncher, appName string) error
DeleteAppConfig removes an app entry from the launcher config and flushes the change. The caller is responsible for stopping the running proc (if any) before calling this — V1 doesn't own the procmanager. Returns an error if the named app isn't in the config so accidental no-ops surface to the operator.
func (*V1) DeriveKeyEntry ¶
DeriveKeyEntry derives the keyring entry for label+index from the visor secret key, deterministic and without mutating the config — for regenerating a known entry (same input → same key).
func (*V1) EffectiveRouteSetupNodes ¶
EffectiveRouteSetupNodes returns the union of deployment route setup nodes (managed by config refresh) and user-added route setup nodes.
func (*V1) EffectiveSurveyWhitelist ¶
EffectiveSurveyWhitelist returns the union of deployment survey whitelist keys (managed by config refresh) and user-added keys.
func (*V1) EffectiveTransportSetupPKs ¶
EffectiveTransportSetupPKs returns the union of deployment transport setup public keys (managed by config refresh) and user-added keys.
func (*V1) GetCalculateRoutes ¶
GetCalculateRoutes gets calculate_routes from routing config
func (*V1) GetLogRotationInterval ¶
GetLogRotationInterval gets log_rotation_interval from config
func (*V1) GetPersistentTransports ¶
func (v1 *V1) GetPersistentTransports() ([]tspec.PersistentTransports, error)
GetPersistentTransports gets persistent_transports from config
func (*V1) MintKey ¶
MintKey derives the NEXT keyring entry (at KeyRing.NextIndex) for the given label, appends it, and advances NextIndex — the wallet "derive a new address" operation. The caller persists with Flush(). Returns the minted entry.
func (*V1) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler for V1. Decodes via v1JSON (which mirrors V1's field set without the embedded sync.RWMutex), copies the result back field-by-field, then patches V1.Pty from the legacy "dmsgpty" key if the canonical "pty" key was absent.
The mirror struct is seeded with v.Common so caller-populated fields like the embedded *Common — set by MakeBaseConfig before the JSON decode runs in ReadRaw — survive the unmarshal.
func (*V1) UpdateAppArg ¶
func (v1 *V1) UpdateAppArg(launch *launcher.AppLauncher, appName, argName string, value interface{}) error
UpdateAppArg updates the cli flag of the specified app config and also within the The updated config gets flushed to file if there are any changes. func (v1 *V1) UpdateAppArg(appName, argName string, value interface{}) error {
func (*V1) UpdateAppArgBatch ¶
func (v1 *V1) UpdateAppArgBatch(launch *launcher.AppLauncher, appName string, args map[string]any) error
UpdateAppArgBatch updates the cli flag of the specified app config and also within the The updated config gets flushed to file if there are any changes. func (v1 *V1) UpdateAppArg(appName, argName string, value interface{}) error {
func (*V1) UpdateAppArgsFull ¶
UpdateAppArgsFull replaces the entire Args slice on the named app. Used by the universal app-settings panel where the operator edits the args as a shell-string and we parse + replace; per-flag UpdateAppArg is for targeted CLI surface, not whole-form save.
func (*V1) UpdateAppAutostart ¶
func (v1 *V1) UpdateAppAutostart(launch *launcher.AppLauncher, appName string, autoStart bool) error
UpdateAppAutostart modifies a single app's autostart value within the config and also the given
The updated config gets flushed to file if there are any changes. func (v1 *V1) UpdateAppAutostart(appName string, autoStart bool) error {
func (*V1) UpdateAppEnv ¶
func (v1 *V1) UpdateAppEnv(launch *launcher.AppLauncher, appName, key, value string) error
UpdateAppEnv sets, replaces, or deletes a KEY=value entry on the named app's Env list. An empty value deletes the entry. The updated config gets flushed to file if anything changed and the launcher is reset so the new env takes effect on the next start.
func (*V1) UpdateAppEnvBatch ¶
func (v1 *V1) UpdateAppEnvBatch(launch *launcher.AppLauncher, appName string, env map[string]string) error
UpdateAppEnvBatch is the multi-key counterpart to UpdateAppEnv. One config flush + one launcher reset for the whole batch. An empty map value deletes that key.
func (*V1) UpdateAppEnvFull ¶
UpdateAppEnvFull replaces the entire Env slice on the named app. Counterpart to UpdateAppArgsFull for the env list. Per-key UpdateAppEnv is still available for targeted mutation; this method is for whole-form save where deletion of keys not in the new list is the intended semantics.
func (*V1) UpdateAppLauncherMode ¶
func (v1 *V1) UpdateAppLauncherMode(launch *launcher.AppLauncher, appName, mode string) error
UpdateAppLauncherMode persists the launcher-mode preference for an app entry. Empty string clears the override (visor default). Validated values: "", "internal", "external".
func (*V1) UpdateAppPort ¶
UpdateAppPort update app port for communicat with visor
func (*V1) UpdateAppSettings ¶
func (v1 *V1) UpdateAppSettings(vals map[string]map[string]int64, text map[string]map[string]string) error
UpdateAppSettings replaces the persisted per-app live knobs wholesale and flushes. An app with neither map is dropped, so a reset leaves no husk.
func (*V1) UpdateAutoconnect ¶
func (v1 *V1) UpdateAutoconnect(set func(*Autoconnect)) error
UpdateAutoconnect applies set to the autoconnect limits and flushes.
func (*V1) UpdateCalculateRoutes ¶
UpdateCalculateRoutes updates calculate_routes in routing config
func (*V1) UpdateHostBridge ¶ added in v1.3.101
func (v1 *V1) UpdateHostBridge(hb *HostBridgeConfig) error
UpdateHostBridge sets the host bridge section, nil to remove it, and saves the config.
func (*V1) UpdateHypervisors ¶
UpdateHypervisors persists the visor's configured hypervisor PKs so a runtime add/remove survives a restart. Same lock+flush shape as UpdateMinHops / UpdatePersistentTransports. A no-op flush on a non-file-backed config (STDIN / in-memory) returns its error to the caller, which may ignore it (the runtime connection change still took effect).
func (*V1) UpdateLogRotationInterval ¶
UpdateLogRotationInterval updates log_rotation_interval in config
func (*V1) UpdateMinHops ¶
UpdateMinHops updates min_hops config
func (*V1) UpdateNetworkPolicy ¶
func (v1 *V1) UpdateNetworkPolicy(p NetworkPolicy) error
UpdateNetworkPolicy sets transport.network and saves the config. The default policy removes the block.
func (*V1) UpdateNetworkWatch ¶
UpdateNetworkWatch sets the network watcher periods and saves the config. A nil value leaves that period unchanged.
func (*V1) UpdatePersistentTransports ¶
func (v1 *V1) UpdatePersistentTransports(pTps []tspec.PersistentTransports) error
UpdatePersistentTransports updates persistent_transports in config
func (*V1) UpdatePtyWhitelist ¶ added in v1.3.99
UpdatePtyWhitelist persists the dmsgpty whitelist. Same lock+flush shape as UpdateHypervisors.
func (*V1) UpdatePublicAutoconnect ¶
UpdatePublicAutoconnect updates public_autoconnect in config
func (*V1) UpdateTCPNotSentLowat ¶
UpdateTCPNotSentLowat sets transport.tcp_notsent_lowat and flushes. nil restores the default.
func (*V1) ValidateResolvers ¶
ValidateResolvers checks the extra resolvers against each other AND against the two primary blocks, and returns the FIRST problem found, naming both sides of it.
The failure this exists to prevent is a silent one. Two SOCKS5 listeners on the same port do not both work and do not both fail: whichever binds first wins, the other's Run loop returns "address already in use" into a log line nobody reads, and the operator sees a resolver that is enabled, shows as running in `app ls`, and answers nothing. Naming the conflict at config time — and refusing to construct the loser at boot — turns that into a sentence an operator can act on.
Only ENABLED entries are port-checked: a disabled resolver binds nothing, so flagging it would fail configs that are perfectly fine today. Everything else (names, kinds, required ports, kind/field mismatches) is checked regardless, since those are wrong whether or not the entry is running.
A wildcard bind ("0.0.0.0" / "::" / "*") conflicts with ANY address on the same port, because it already covers loopback — "0.0.0.0:4445" and "127.0.0.1:4445" are not two listeners, they are one bind and one EADDRINUSE.
func (*V1) WalletCustody ¶
WalletCustody returns the effective custody mode, defaulting to browser and clamping to what this build can actually realize.
The clamp is the point of the capability gating: disk needs a backend process with a filesystem. Under wasm there is none, so a config carrying custody:"disk" — perfectly valid on the native visor that wrote it — silently degrades to browser instead of leaving the UI offering a mode that cannot work. See walletCustodyDiskCapable (per-build).
func (*V1) WalletDir ¶
WalletDir returns the disk-custody wallet directory (empty unless disk custody is both selected and realizable).
func (*V1) WalletRemotePK ¶
WalletRemotePK returns the remote-custody target (empty unless remote).
func (*V1) WalletServe ¶
WalletServe reports whether the wallet frontend should be served. Nil-safe: a config predating this block (or with the block omitted) serves the wallet, preserving existing behavior — the block is opt-OUT, not opt-in.
type WTPeer ¶
WTPeer is a statically-configured WebTransport peer (transport.wt_table): the peer's https:// WebTransport endpoint and the lowercase-hex SHA-256 of its self-signed cert, which the dialer pins (WT has no CA).
type WalletConfig ¶
type WalletConfig struct {
// Serve controls whether the /wallet/ frontend is served at all — the
// feature on/off switch. Deliberately a CLI/config knob rather than a GUI
// self-toggle: a GUI that can turn off the surface hosting it is a trap.
Serve bool `json:"serve"`
// Custody is browser | disk | remote (the WalletCustody* constants).
// Empty means browser. A value the current context cannot realize is
// clamped by WalletCustody() rather than failing the config load, so a
// config written on a native visor still loads under wasm.
Custody string `json:"custody,omitempty"`
// Dir is the disk-custody wallet directory. It is a SEED-wallet store —
// one directory holding N seed files — NOT one directory per coin. The
// per-coin-directory layout is legacy: since bip44 carries coin_type per
// account, one seed file already spans several chains.
Dir string `json:"dir,omitempty"`
// User is the account to drop to when running the wallet server, for
// visors running as a system user (_skywire) that should not own the
// operator's wallet files. Mirrors SKYCOINWEBUSER. Disk custody only.
User string `json:"user,omitempty"`
// RemotePK is the visor whose skycoin-web server holds the wallets, for
// remote custody.
RemotePK string `json:"remote_pk,omitempty"`
}
WalletConfig is the single config block behind the wallet feature, in all three realizations (native visor, `hv serve`, wasm/single-file). The block is uniform; what DEGRADES per context is which custody modes can actually be realized — a context with no backend filesystem offers only browser|remote. Keeping one block rather than per-context knobs is what stops the surfaces drifting apart.
The existing skycoinweb* config-gen flags (--skycoinweb, --skycoinwebwallet, --skycoinwebaddr, --skycoinwebnodes, --skycoinwebuser) become aliases into this block rather than a parallel source of truth.
type WasmServeConf ¶
type WasmServeConf struct {
Addr string `json:"addr"` // e.g. ":8443"; empty = disabled
TLS bool `json:"tls,omitempty"` // HTTPS with a self-signed localhost cert (or TLSCert/TLSKey if set)
TLSCert string `json:"tls_cert,omitempty"` // optional PEM cert — a locally-trusted *.mesh.localhost cert (mkcert) so real-origin browse iframes load without a per-host accept
TLSKey string `json:"tls_key,omitempty"` // optional PEM key, paired with TLSCert
Harness bool `json:"harness,omitempty"` // mount the /ctl/* operator control bridge (DEV ONLY — never expose publicly)
Password string `json:"password,omitempty"` // optional access-password gate (use with TLS)
// ExecWasm is the path to the FULL skywire CLI built for GOOS=js, served at
// /skywire.wasm: the desk host, the tab's visor and every command the
// desk's terminal runs. Empty means the module kept beside the binary,
// which is fetched and refreshed when the desk is opened. A path here is a
// developer's own build and is served as it is. Same as
// `skywire cli hv serve --exec-wasm`.
ExecWasm string `json:"exec_wasm,omitempty"`
// DeskHelpTerminal opens a second desk terminal that has already run
// `skywire --help`. Off by default: it is a whole extra Go/wasm runtime of
// the full binary, and that memory is never returned to the browser. See
// pkg/visor.deskWasmBootOpts for the measurement.
DeskHelpTerminal bool `json:"desk_help_terminal,omitempty"`
// DeskDocsPort runs `skywire doc serve` on this virtual-loopback port of the
// desk. 0 (the default) = off, for the same reason.
DeskDocsPort int `json:"desk_docs_port,omitempty"`
// BrowseSuffix is the real-origin browser's browse-origin domain suffix
// (leading dot); empty = ".mesh.localhost".
BrowseSuffix string `json:"browse_suffix,omitempty"`
// BrowseOriginAddr, when set, ALSO serves the browse-origin SW bootstrap on
// this second address (Caddy fronts it on *.<browse_suffix>). Empty = off.
BrowseOriginAddr string `json:"browse_origin_addr,omitempty"`
// VOrigin is the public origin of the visor app V (e.g.
// "https://theskywirenetwork.net") that B's bootstrap postMessages to — used
// only with BrowseOriginAddr behind a proxy. Empty = derive from Addr.
//
// Comma-separated for several apps sharing one browse domain, or "*" to
// accept any parent. It is the only thing deciding who may feed content
// into a browse origin, so "*" is a decision: an uninvited page cannot
// reach a visitor's browsing state (storage is partitioned by top-level
// site) but it can serve its own content from a subdomain of yours.
VOrigin string `json:"browse_v_origin,omitempty"`
}
WasmServeConf configures the in-process standalone wasm-visor server — the same surface as `skywire cli hv serve`, hosted by the visor daemon. See pkg/visor.ServeWasm.
type WispConfig ¶
type WispConfig struct {
// UpstreamSOCKS is the SOCKS5 proxy that carries the streams. Empty
// means the local skysocks-client.
UpstreamSOCKS string `json:"upstream_socks,omitempty"`
// Buffer is the per-stream client -> server credit, in packets. Zero
// means the package default.
Buffer uint32 `json:"buffer,omitempty"`
}
WispConfig tunes the embedded Wisp server: a multiplexer that carries many TCP and UDP sockets for a page over one connection, which is how a browser-side Linux guest is given a network.
It binds the page's virtual loopback rather than an HTTP port, and frames its session rather than upgrading it to a WebSocket. Both are forced: inside a tab websocket.Accept does not exist, and a service worker cannot intercept ws:// even if it did. So the page reaches this by dialing the vnet port directly.
Streams leave through UpstreamSOCKS, which defaults to the visor's own skysocks-client — putting a guest's traffic on a route to an exit instead of through whatever central relay the page shipped with.
The server runs on every browser-tab visor and never on a native one, where nothing can reach it: a browser there has no raw TCP, and `skywire cli wisp serve` is the WebSocket backend for it. This section only tunes it.
Source Files
¶
- args.go
- args_native.go
- autoconnect.go
- common.go
- common_native.go
- config.go
- config_compat.go
- deployment_services.go
- hypervisorconfig.go
- hypervisorconfig_native.go
- keyring.go
- network_watch.go
- parse.go
- preserve.go
- read.go
- resolvers.go
- services.go
- services_native.go
- survey_linux.go
- survey_whitelist.go
- tcp_notsent.go
- types.go
- types_native.go
- v1.go
- v1_runtime.go
- values.go
- values_linux.go
- values_native.go
- walletconfig.go
- walletconfig_native.go