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

visorapi

package
v1.3.98 Latest Latest
Warning

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

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

Documentation

Overview

Package visorapi pkg/visor/visorapi/api.go c3-vis-core

Package visorapi pkg/visor/visorapi/args.go c3-vis-core

Package visorapi pkg/visor/visorapi/client.go c3-vis-core

Package visorapi pkg/visor/visorapi/cxo.go c3-vis-core

Package visorapi pkg/visor/visorapi/dmsg.go c3-vis-core

Package visorapi pkg/visor/visorapi/doc.go c3-vis-core

Package visorapi is the visor's RPC contract: the API interface, the types its methods take and return, and the net/rpc client that implements it against a running visor.

It exists so that callers of a visor (the CLI, apps, services) can depend on the contract without linking the visor itself. pkg/visor implements API and serves it; nothing here imports pkg/visor.

Package visorapi pkg/visor/visorapi/group.go c3-vis-core

Package visorapi pkg/visor/visorapi/hypervisor.go c3-vis-core

Package visorapi pkg/visor/visorapi/local.go c3-vis-core

Package visorapi pkg/visor/visorapi/mail.go c3-vis-core

Package visorapi pkg/visor/visorapi/mock.go c3-vis-core

Package visorapi pkg/visor/visorapi/pairing.go c3-vis-core

Package visorapi pkg/visor/visorapi/routing.go c3-vis-core

Package visorapi pkg/visor/visorapi/services.go

Package visorapi pkg/visor/visorapi/state.go c3-vis-core

Package visorapi pkg/visor/visorapi/stats.go c3-vis-core

Package visorapi pkg/visor/visorapi/voice.go c3-vis-core

Index

Constants

View Source
const (
	// VisorSCPUpload pushes a local file to the remote host's rootDir.
	VisorSCPUpload = "upload"
	// VisorSCPDownload pulls a file from the remote host's rootDir
	// into a local path.
	VisorSCPDownload = "download"
)

VisorSCPDirection enumerates the two scp transfer directions.

View Source
const (
	// VisorSCPTransportDmsg dials peer:port over dmsg.
	VisorSCPTransportDmsg = "dmsg"
	// VisorSCPTransportSkynet dials peer:port over the skywire router
	// (appnet TypeSkynet). Routing through skynet keeps the bytes off
	// the dmsg overlay and lets the transfer ride whatever transports
	// the router picks for the route group.
	VisorSCPTransportSkynet = "skynet"
)

VisorSCPTransport enumerates the transports the visor will dial the peer's dmsgscp host over. Both peers expose the host on dmsg AND skynet at the same port; the caller picks per-call.

View Source
const (
	// VisorCatModeDial opens an outbound stream to (RemotePK, Port)
	// over the chosen transport. The visor exposes the stream to the
	// CLI as a 127.0.0.1 loopback TCP listener — the CLI dials that
	// listener and the visor splices the two halves.
	VisorCatModeDial = "dial"
	// VisorCatModeListen opens a one-shot listener on Port over the
	// chosen transport (dmsg + skynet mirror), authorizes the first
	// inbound stream against the dmsgscp whitelist, and splices it
	// to a 127.0.0.1 loopback the CLI dials.
	VisorCatModeListen = "listen"
)

VisorCatMode enumerates the two cat operation modes.

View Source
const (
	// VisorCatTransportDmsg dials/listens over dmsg.
	VisorCatTransportDmsg = "dmsg"
	// VisorCatTransportSkynet dials/listens over the skywire router
	// (appnet.TypeSkynet).
	VisorCatTransportSkynet = "skynet"
)

VisorCatTransport enumerates the transports the visor will use for a VisorCat call — identical to VisorSCP's set so a caller's `--transport=skynet` keeps the same meaning across both commands.

View Source
const (
	SelectSummary    = "summary"    // summary
	SelectHealth     = "health"     // health + service_health
	SelectRouting    = "routing"    // routing_stats + route_groups + routing_policy + router_config
	SelectMux        = "mux"        // mux_route_groups + mux_counters (+ route_groups count)
	SelectPool       = "pool"       // pool: the standby-tunnel pool table
	SelectApps       = "apps"       // apps
	SelectTransports = "transports" // transports + persistent_transports
	SelectModules    = "modules"    // modules
	SelectCXO        = "cxo"        // cxo feed publish-health
	SelectServices   = "services"   // deployment services the visor embeds: stores, CXO aggregators and publishers
	SelectProxy      = "proxy"      // visor-side proxystatus snapshot (skysocks); opt-in only
	SelectDiag       = "diag"       // router intake, transport queues/handlers, vstream, dmsg ping/relay, runtime
	SelectRoles      = "roles"      // in-process dmsg server, dmsg relay (both directions), transit refusal
)

State*-select keys name the projectable subtrees of a StateSnapshot. Passing a subset to StateSnapshotProjected makes the SERVER build and marshal ONLY those sections, so a cheap `--select mux` skips the expensive transports build (the full snapshot is ~900 KB, dominated by transports at ~307 KB; mux is ~75 KB). The keys match the snapshot's JSON field names (or a short alias) so a --jq expression written against the full snapshot transfers to a projection unchanged.

View Source
const (
	// RPCPrefix is the prefix used with all RPC calls.
	RPCPrefix = "app-visor"
)
View Source
const SkyenvRedacted = "(set)"

SkyenvRedacted is what Skyenv reports in place of a secret's value, and what SetSkyenv ignores when a form sends it back unchanged.

View Source
const (
	// SupportedProtocolVersion is the app protocol version a visor reports.
	SupportedProtocolVersion = "0.1.0"
)

Variables

View Source
var (
	// ErrNotImplemented occurs when a method is not implemented.
	ErrNotImplemented = errors.New("not implemented")
)

StateSelectKeys is the documented set of --select keys, in help order.

Functions

func HypervisorFingerprint

func HypervisorFingerprint(pk cipher.PubKey) string

HypervisorFingerprint is the short, stable name a pending key is approved by: the first 40 bits of sha256(pk) as two hex groups ("a1b2c-3d4e5").

func RegisterLocalAPI

func RegisterLocalAPI(api API)

RegisterLocalAPI publishes api as the visor running in this process, replacing any previous registration (a visor restart in the same process re-registers, the way the handler registry re-registers on an app restart).

func SkyenvSecret

func SkyenvSecret(key string) bool

SkyenvSecret reports variables whose value never leaves the visor: Skyenv reports only that they are set.

func UnregisterLocalAPI

func UnregisterLocalAPI(api API)

UnregisterLocalAPI clears the registration only if it still holds api.

Compare-and-clear, not a plain clear: a visor shutting down can finish unwinding after a replacement has already registered (tests build visors back to back in one process), and an unconditional clear there would take the live visor out and leave in-process apps dialing a port nobody serves.

Types

type API

API is everything a visor answers over RPC. Callers that need one part of it can take the narrower interface it embeds.

func LocalAPI

func LocalAPI() API

LocalAPI returns the visor running in this process, or nil when there is none and the caller must dial the RPC.

Close on the returned value is a no-op. Callers treat what they get as an RPC *client* and close it when they redial — doing that to the visor itself would shut the process down.

func NewMockRPCClient

func NewMockRPCClient(r *rand.Rand, maxTps int, maxRules int) (cipher.PubKey, API, error)

NewMockRPCClient creates a new mock API.

func NewProxyRPCClient

func NewProxyRPCClient(log logrus.FieldLogger, conn io.ReadWriteCloser, prefix string, timeout time.Duration, remotePK cipher.PubKey) API

NewProxyRPCClient wraps a local rpc.Client connection so that every Call against the returned API is transparently routed to a remote visor via the local visor's TransportRPCCall method.

Use case: `skywire cli visor info --rpc skynet://<pk>` — the CLI dials the LOCAL visor as usual, but every typed method (Summary, Health, Overview, ...) gets dispatched on the remote visor identified by remotePK. Auth: the local visor's PK must be in the remote's hypervisor or dmsgpty whitelist (TransportRPCServer enforces this on the receiving side).

Limitation: methods with structured Go args (AddTransport, SetMinHops, etc.) don't currently survive the TransportRPCCall JSON-bytes wire format. No-arg query methods (the bulk of what the CLI does) work directly.

func NewRPCClient

func NewRPCClient(log logrus.FieldLogger, conn io.ReadWriteCloser, prefix string, timeout time.Duration) API

NewRPCClient creates a new API.

type ARSelfEntry

type ARSelfEntry struct {
	Type         string   `json:"type"`                     // "stcpr", "sudph" or "wt"
	RemoteAddr   string   `json:"remote_addr,omitempty"`    // public IPv4 as AR sees the visor
	RemoteAddrV6 string   `json:"remote_addr_v6,omitempty"` // public IPv6 as AR sees the visor (#1525 Phase 4a)
	Port         string   `json:"port,omitempty"`           // listen port
	Addresses    []string `json:"addresses,omitempty"`      // local interface addresses the visor advertised
	// CertHash is the SHA-256 of the self-signed certificate a WebTransport
	// dialler must pin, lowercase hex. WT only — the other carriers have no
	// certificate. Empty from a visor too old to report it, which is why the
	// CLI omits the field rather than rendering "(none)": absent here means
	// "not said", and only a visor that knows about WT can distinguish that
	// from "not registered".
	CertHash string `json:"cert_hash,omitempty"`
}

ARSelfEntry is one transport-type-row of this visor's own AR registration: the IP:port it is reachable on according to the address-resolver.

RemoteAddrV6 is the optional IPv6 counterpart of RemoteAddr, populated when this visor has bound the AR over an IPv6 HTTP client (#2719's secondary bind). Empty for v4-only deployments and on older AR servers that don't capture family — same backward-compat shape as the underlying addrresolver.VisorData (#2715).

type ARSelfRegistration

type ARSelfRegistration struct {
	Entries []ARSelfEntry `json:"entries,omitempty"`
	// Queried lists the transport types this visor actually checked, whether or
	// not it is registered for them. It exists so a caller can tell "asked, not
	// registered" from "never asked".
	//
	// Without it the two are the same silence, and the CLI cannot say which it
	// is looking at: a type missing from Entries might be unregistered, or the
	// answering visor might predate that type entirely. That is not
	// hypothetical here — `--via dmsg://<pk>` and `--rpc` point these commands
	// at other people's visors across the mesh, running whatever version they
	// are running. Locally it cannot happen, because the CLI and the visor are
	// the same binary.
	//
	// Empty from a visor too old to report it, which is itself the signal:
	// nothing can be concluded from a type's absence.
	Queried []string `json:"queried,omitempty"`
}

ARSelfRegistration is the visor's own AR record across transport types. One ARSelfEntry per transport the visor is currently registered for. Empty when the visor has not (yet) bound any transport with AR.

type AddPtyWhitelistIn

type AddPtyWhitelistIn struct {
	PKs []cipher.PubKey
}

AddPtyWhitelistIn carries the PKs to merge into the visor's shared peer whitelist (a hypervisor pushing its own hypervisors).

type AddTransportIn

type AddTransportIn struct {
	RemotePK         cipher.PubKey
	TpType           string
	Timeout          time.Duration
	Label            string // "user" or "skycoin" (default: "skycoin")
	NoRegister       bool   // skip transport discovery registration (only valid for "user" label)
	SkipLatencyProbe bool   // deprecated: latency is now measured at the transport level
}

type AppLogsRequest

type AppLogsRequest struct {
	// TimeStamp should be time.RFC3339Nano formatted
	TimeStamp time.Time `json:"time_stamp"`
	// AppName should match the app name in visor config
	AppName string `json:"app_name"`
}

type AppNameIn

type AppNameIn struct {
	AppName string
}

AppNameIn carries a single app name for RPCs whose only argument is the target's name (DeleteApp, etc.).

type AppRouteStatus

type AppRouteStatus struct {
	AppName string             `json:"app_name"`
	Route   router.RouteStatus `json:"route"`
}

AppRouteStatus combines route status with the app that owns it.

type AppSettings

type AppSettings struct {
	AppName string           `json:"app_name"`
	Values  map[string]int64 `json:"values,omitempty"`
	// Text carries the LIST knobs (pool.exclude_pks, pool.require_tp_types),
	// whose payload is a comma-separated set of tokens rather than an int64.
	Text    map[string]string `json:"text,omitempty"`
	Version uint64            `json:"version"`
	Applied uint64            `json:"applied"`
}

AppSettings is the CLI's read of an app's live tuning knobs: the values the visor intends, the version they carry, and the version the app last reported having installed. Version != Applied is a change still in flight — the app installs it on its next pull (one tunnel.probe_interval, 5 s by default).

type Apps

type Apps interface {
	App(appName string) (*appserver.AppState, error)
	Apps() ([]*appserver.AppState, error)
	StartApp(appName string) error
	StartAppWithMode(appName, launcherMode string) error
	// SetAppRoutingPolicy installs (or clears, when path is "" /
	// "none") a per-app routing policy. Backend is dispatched by
	// file extension: "@/path.star" uses Starlark, "@/path.wasm"
	// uses WASM. The swap is live — the running app picks up the
	// new policy on its next dial without a restart.
	SetAppRoutingPolicy(appName, path string) error
	AddApp(appName, binaryName string) error
	DeleteApp(appName string) error
	RegisterApp(procConf appcommon.ProcConfig) (appcommon.ProcKey, error)
	DeregisterApp(procKey appcommon.ProcKey) error
	StopApp(appName string) error
	KillApp(appName string) error
	SetAppDetailedStatus(appName, state string) error
	SetAppError(appName, stateErr string) error
	RestartApp(appName string) error
	SetAutoStart(appName string, autostart bool) error
	SetAppWhitelist(appName, whitelist string) error
	// AddPtyWhitelist merges PKs into the visor's shared peer
	// whitelist (a connected hypervisor pushing its own hypervisors
	// for transitive pty + RPC trust).
	AddPtyWhitelist(pks []cipher.PubKey) error
	SetAppPK(appName string, pk cipher.PubKey) error
	SetAppSecure(appName string, isSecure bool) error
	SetAppAddress(appName string, address string) error
	SetAppKillswitch(appName string, killswitch bool) error
	SetAppNetworkInterface(appName string, netifc string) error
	SetAppDNS(appName string, dnsaddr string) error
	DoCustomSetting(appName string, customSetting map[string]any) error
	SetAppArgs(appName string, args []string) error
	// GetAppSettings / SetAppSettings carry the live tuning knobs held for an
	// app (pulled by the app, persisted by the visor; see api_app_settings.go).
	GetAppSettings(appName string) (AppSettings, error)
	SetAppSettings(appName string, vals map[string]int64, text map[string]string) (AppSettings, error)
	// CutAppTunnel closes exactly one of an app's tunnels — the route group
	// whose port is rgPort — and lets the app's own pool replace it.
	CutAppTunnel(appName string, rgPort uint16) (uint64, error)
	SetAppEnv(appName, key, value string) error
	SetAppEnvBatch(appName string, env map[string]string) error
	SetAppEnvFull(appName string, env []string) error
	SetAppLauncherMode(appName, mode string) error
	AppHelp(appName string) (string, error)
	LogsSince(timestamp time.Time, appName string) ([]string, error)
	RecentAppLog(appName, level string) ([]string, error)
	GetAppStats(appName string) (appserver.AppStats, error)
	GetAppError(appName string) (string, error)
	GetAppConnectionsSummary(appName string) ([]appserver.ConnectionSummary, error)
	StartVPNClient(pk cipher.PubKey) error
	StartVPNClientWithMode(pk cipher.PubKey, launcherMode string) error
	StopVPNClient(appName string) error
	VPNServers(version, country string) ([]servicedisc.Service, error)
	StartSkysocksClient(pk string) error
	StopSkysocksClients() error
	ProxyServers(version, country string) ([]servicedisc.Service, error)
	TestProxy(config ProxyTestConfig) ([]ProxyTestResult, error)
	// AppDirectStreams reports the live DIRECT (0-hop) streams the named
	// app holds — the AppDirectMux shortcut's counterpart to
	// RouteGroupMuxInfo. A dial eligible for that shortcut builds no route
	// group, so an app using it has a well-defined path that the
	// route-group queries above cannot see. Empty appName reports all.
	AppDirectStreams(appName string) ([]transport.VStreamInfo, error)
}

Apps is starting, stopping and configuring apps, the VPN and skysocks clients among them.

type BandwidthResult

type BandwidthResult struct {
	BytesSent     uint64        `json:"bytes_sent"`
	BytesReceived uint64        `json:"bytes_received"`
	Duration      time.Duration `json:"duration"`
	UploadSpeed   float64       `json:"upload_speed_kbps"`   // KB/s
	DownloadSpeed float64       `json:"download_speed_kbps"` // KB/s
}

BandwidthResult contains the results of a bandwidth test

type BandwidthTestConfig

type BandwidthTestConfig struct {
	PK         cipher.PubKey
	Duration   time.Duration // How long to run the test
	PacketSize int           // Size of each packet in KB
	LocalRoute bool          // Use local route calculation
}

BandwidthTestConfig contains parameters for bandwidth testing

type CXO

type CXO interface {
	FetchCXO(args FetchCXOArgs) (*FetchCXOResult, error)
	CXOStatus() ([]FeedStatus, error)
	CXORefreshFeed(args CXORefreshArgs) (*FeedStatus, error)
	// CXO user feeds — visor-published TreeStore feeds beyond the
	// always-on telemetry one. See pkg/visor/cxo_user_feeds.go.
	RegisterCXOFeed(name string, dmsgPort uint16, description string) error
	UnregisterCXOFeed(name string) error
	ListCXOFeeds() []logserver.CXOFeedEntry
}

CXO is the visor's CXO feeds.

type CXOFeedState

type CXOFeedState struct {
	Name string `json:"name"`
	Port uint16 `json:"port,omitempty"`
	treestore.PublishState
	// CurrentLeaves is populated only for the "stats" telemetry feed: it
	// reports the count of per-transport telemetry rows the feed carries
	// across its ≤16 compact sharded leaves (transports/telemetry/<sh>).
	// With the sharded shape every packed row is a LIVE transport (the
	// sampler re-encodes each shard from the live set every tick), so Live
	// == Total and Dead is structurally 0 — the stale-leaf bloat the old
	// per-transport `current` format accumulated no longer exists.
	CurrentLeaves *CurrentLeafStats `json:"current_leaves,omitempty"`
	// Allowlist is the set of PKs currently permitted to subscribe to this
	// feed (nil = OPEN to all). For a service-consumed feed (stats,
	// tp-list, registration) this MUST contain the consuming service's PK
	// or that service can't fill — its subscribe is rejected and it shows
	// up under Denied below.
	Allowlist []string `json:"allowlist,omitempty"`
	// Denied lists would-be subscribers the allowlist turned away
	// (most-recent first). A denied PK that isn't a known peer is the
	// direct signature of a gating misconfiguration — e.g. TPD dialing in
	// under a CXO node key that differs from the transport_discovery_dmsg
	// PK the visor allowlisted.
	Denied []DeniedSubscriber `json:"denied,omitempty"`
}

CXOFeedState pairs a feed's identity (name + dmsg port) with its live publish-health snapshot. Surfaced by StateSnapshot under .cxo.

type CXORefreshArgs

type CXORefreshArgs struct {
	Feed    string        `json:"feed"`
	Timeout time.Duration `json:"timeout,omitempty"`
}

CXORefreshArgs identifies which feed CXORefreshFeed should force a synchronous subscribe → first-Root → walk cycle on. Timeout caps how long the visor will wait for the first Root before giving up; zero means "use the manager's firstSyncTimeout default" (10s).

type Chat

type Chat interface {
	SkychatPasswordIsSet() (bool, error)
	SetSkychatPassword(oldPassword, newPassword string) error
	ClearSkychatPassword(oldPassword string) error
	SkychatLocalAddr() (string, error)
	// Chat-pair feeds — per-partner CXO feeds with read-side
	// allowlists. See pkg/visor/pairing.go.
	PairAdd(peerPK cipher.PubKey) error
	PairList() ([]PairInfo, error)
	PairRemove(peerPK cipher.PubKey) error
	PairMarkActive(peerPK cipher.PubKey) error
	PairSend(peerPK cipher.PubKey, text string) (string, error)
	PairDelete(peerPK cipher.PubKey, id string) error
	PairPoll(since time.Time) ([]PairMessage, error)
	// Skychat profile — this visor's published display name and avatar,
	// and a peer's. See pkg/visor/profile.go.
	ProfileGet() (Profile, error)
	ProfileSet(args ProfileSetArgs) (Profile, error)
	ProfileFetch(pk cipher.PubKey) (Profile, error)
	// Chat-group feeds — D1 owner-centric CXO feeds with multi-PK
	// allowlists. See pkg/visor/group.go.
	GroupCreate(args GroupCreateArgs) (GroupInfo, string, error)
	GroupJoin(args GroupJoinArgs) (GroupInfo, error)
	GroupResolve(args GroupResolveArgs) (GroupResolveResult, error)
	GroupAskAgain(id string) (GroupInfo, error)
	GroupList() ([]GroupInfo, error)
	GroupGet(id string) (GroupInfo, error)
	GroupInvite(id string) (string, error)
	GroupAddMember(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupJoinRequests(id string) ([]GroupJoinRequest, error)
	GroupApproveJoin(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupDenyJoin(id string, pk cipher.PubKey) error
	GroupRemoveMember(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupBanMember(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupUnbanMember(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupMuteMember(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupUnmuteMember(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupSetReadOnly(id string, readOnly bool) (GroupInfo, error)
	GroupSetListed(id string, listed bool) (GroupInfo, error)
	GroupSetMeta(args GroupSetMetaArgs) (GroupInfo, error)
	GroupRefreshMeta(id string) (GroupInfo, error)
	GroupCatalog(host cipher.PubKey) ([]GroupCatalogEntry, bool, error)
	GroupPromoteAdmin(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupDemoteAdmin(id string, pk cipher.PubKey) (GroupInfo, error)
	GroupRotateKey(id string) (GroupInfo, error)
	GroupSetPeerBackfill(id string, enabled bool) (GroupInfo, error)
	GroupSetJoinPoW(id string, bits uint8) (GroupInfo, error)
	GroupSend(args GroupSendArgs) error
	GroupFileKey(args GroupFileKeyArgs) (GroupFileKeyResult, error)
	GroupUnsend(args GroupUnsendArgs) error
	GroupPoll(since time.Time) ([]GroupMessage, error)
	GroupDelete(id string) error
	GroupLeave(id string) error
	GroupHistory(groupID string, limit int) ([]GroupMessage, error)
	GroupHistoryPage(args GroupHistoryPageArgs) ([]GroupMessage, error)
	GroupHistoryGroups() ([]string, error)
	// Skychat 1:1 voice calls (pkg/skychat/call).
	VoiceCall(peer cipher.PubKey) (string, error)
	// VoiceDial places a call and returns its id without waiting for an
	// answer — what a UI needs, and what an HTTP handler can actually
	// deliver. See Visor.VoiceDial.
	VoiceDial(peer cipher.PubKey) (string, error)
	VoiceHangup(callID string) error
	VoiceActive() ([]string, error)
	VoiceAnswer(callID string) error
	VoiceDecline(callID string) error
	VoiceIncoming() ([]string, error)
	// VoiceDialing lists the calls this visor is PLACING and that have not
	// been answered yet. It is what lets a UI show "calling…" and, more to
	// the point, call it off: hanging up takes a call id, and until the
	// invite is answered this is the only place one exists.
	VoiceDialing() ([]VoiceDialingInfo, error)
	// VoiceSetRingback sets the tone callers hear while this visor rings;
	// empty Data clears it. It is kept across restarts.
	VoiceSetRingback(tone VoiceRingback) error
	// VoiceRingback returns this visor's own ringback tone.
	VoiceRingback() (VoiceRingback, error)
	// VoiceDialRingback returns the ringback tone the peer of an outbound
	// call plays, once it has arrived (VoiceDialingInfo.Ringback).
	VoiceDialRingback(callID string) (VoiceRingback, error)
	VoiceCallAudio(callID string) (sent, recv []int16, err error)
	VoiceMute(callID string, mic, speaker bool) error
}

Chat is skychat's pairing, profiles, groups and voice calls.

type ConfigFieldChange

type ConfigFieldChange struct {
	// Path is the dotted config path as the caller supplied it.
	Path string `json:"path"`
	// Old and New are the field's JSON values before and after.
	// Old is null when the path addressed an absent map key.
	Old json.RawMessage `json:"old"`
	New json.RawMessage `json:"new"`
	// Live is true when a running subsystem took the value; false
	// means it is on disk and applies at the next visor start.
	Live bool `json:"live"`
}

ConfigFieldChange reports one field's before/after from SetConfigFields.

func (ConfigFieldChange) String

func (c ConfigFieldChange) String() string

String renders one change the way `config set` prints it.

type ConnectIn

type ConnectIn struct {
	// Network selects the underlying transport for the reverse proxy.
	// "skynet" (default; empty string treated as skynet) or "dmsg".
	Network    string
	RemotePK   cipher.PubKey
	RemotePort int
	LocalPort  int
}

type CurrentLeafStats

type CurrentLeafStats struct {
	Total int `json:"total"`
	Live  int `json:"live"`
	Dead  int `json:"dead"`
}

CurrentLeafStats is the live/dead breakdown of the telemetry feed's per-transport telemetry rows, decoded from the compact sharded leaves (transports/telemetry/<sh>). Each row is classified by whether its transport is in the visor's live set; with the sharded shape the sampler only ever packs live transports, so Dead is normally 0.

type CutAppTunnelIn

type CutAppTunnelIn struct {
	AppName string
	RGPort  uint16
}

CutAppTunnelIn names one tunnel of one app: the route group port `proxy mux info` prints as dst_port.

type DMSGServerInfo

type DMSGServerInfo struct {
	PK      cipher.PubKey `json:"pk"`
	Latency time.Duration `json:"latency"` // Round-trip latency via self-ping, 0 if not measured
	// Carrier is the transport the client↔server session runs on (tcp | ws | wt |
	// quic); Protocol is its human-readable label (e.g. "tcp", "wss",
	// "webtransport", "quic"). Empty when the session can't be matched. Lets the
	// UI show HOW each server was reached, not just that it was.
	Carrier  string `json:"carrier,omitempty"`
	Protocol string `json:"protocol,omitempty"`
	// Streams is the number of open mux streams on this session: 0 = idle (a
	// candidate for the idle-session reaper once it is over min_sessions), >0 =
	// carrying traffic, -1 = unmeasurable (quic, or no session matched this
	// server PK) which the reaper treats as busy. This is the same count
	// reapExcessIdleSessions decides on, so an operator seeing more sessions
	// than sessions_count can tell whether they are legitimately in use or the
	// reaper is not doing its job.
	Streams int `json:"streams"`
}

DMSGServerInfo contains information about a connected DMSG server including latency.

type DeniedSubscriber

type DeniedSubscriber struct {
	PK     string    `json:"pk"`
	Count  int       `json:"count"`
	LastAt time.Time `json:"last_at"`
}

DeniedSubscriber is one rejected-subscriber record for CXOFeedState.

type DeregisterServiceIn

type DeregisterServiceIn struct {
	PKs         []cipher.PubKey
	ServiceType string
}

type DiagDmsg

type DiagDmsg struct {
	// Unpublished: this client publishes no discovery entry (a browser visor).
	Unpublished   bool              `json:"unpublished"`
	Sessions      []DiagDmsgSession `json:"sessions,omitempty"`
	RelayNominees []cipher.PubKey   `json:"relay_nominees,omitempty"`
	RelayingFor   []DiagRelayClient `json:"relaying_for,omitempty"`
	RelayBackoff  []DiagRelayTimer  `json:"relay_backoff,omitempty"`
	RelayDialSkip []DiagRelayTimer  `json:"relay_dial_skip,omitempty"`
}

DiagDmsg is the dmsg client's session and relay state.

type DiagDmsgSession

type DiagDmsgSession struct {
	PK        cipher.PubKey `json:"pk"`
	Carrier   string        `json:"carrier"`
	Protocol  string        `json:"protocol"`
	Streams   int           `json:"streams"`
	LatencyMS float64       `json:"latency_ms"`
	// PingFails is the consecutive liveness-ping failures; 2 closes the session.
	PingFails int `json:"ping_fails"`
}

DiagDmsgSession is one dmsg session's liveness.

type DiagPanics

type DiagPanics struct {
	Count uint64               `json:"count"`
	Last  []logging.PanicEntry `json:"last,omitempty"`
}

DiagPanics is the recovered-panic accounting. Count keeps rising after Last has wrapped, so the two together say whether what Last shows is the whole story.

type DiagQueue

type DiagQueue struct {
	Depth    int `json:"depth"`
	Capacity int `json:"capacity"`
}

DiagQueue is a bounded queue's depth and capacity.

type DiagRelayClient

type DiagRelayClient struct {
	PK      cipher.PubKey `json:"pk"`
	Streams int           `json:"streams"`
}

DiagRelayClient is a peer attached to this visor as its dmsg relay.

type DiagRelayTimer

type DiagRelayTimer struct {
	PK         cipher.PubKey `json:"pk"`
	RemainingS float64       `json:"remaining_s"`
}

DiagRelayTimer is a relay-side backoff or skip with its remaining time.

type DiagRuntime

type DiagRuntime struct {
	Goroutines  int     `json:"goroutines"`
	HeapAllocMB float64 `json:"heap_alloc_mb"`
	SysMB       float64 `json:"sys_mb"`
	NumGC       uint32  `json:"num_gc"`
	GoVersion   string  `json:"go_version"`
}

DiagRuntime is the Go runtime at a glance.

type DiagSnapshot

type DiagSnapshot struct {
	Runtime DiagRuntime `json:"runtime"`
	// Panics is the recovered-panic accounting: how many have happened since
	// start and the most recent few with their stacks. An UNrecovered panic
	// goes to local/log/skywire-crash.log (debug.SetCrashOutput, see
	// storeLog); this is the other half, which nothing else records. Omitted
	// when none have happened, so its presence is the signal.
	Panics *DiagPanics `json:"panics,omitempty"`
	// TransportReadQueue is the shared queue every transport read loop feeds
	// and the router drains; at capacity, every transport stalls behind the
	// router (pings and pongs included).
	TransportReadQueue *DiagQueue `json:"transport_read_queue,omitempty"`
	// Intake is the router's inbound-path view: unknown/control packets by
	// type, stale-route drops, and each route group's app queue depth.
	Intake *router.IntakeStats `json:"intake,omitempty"`
	// RouteSource is where multi-hop routes came from: the local graph (a
	// hypervisor's attached visors), the route finder, or the local fallback.
	RouteSource *router.RouteSourceStats `json:"route_source,omitempty"`
	// RouteSetup is how this visor's route setups were ISSUED: batched vs
	// single vs fell back because the setup node is un-upgraded, plus whether
	// the RSN-oracle queried once per fill and whether the concurrent dials of a
	// fill landed on distinct intermediates.
	RouteSetup *router.SetupPathStats `json:"route_setup,omitempty"`
	// VStream is one entry per virtual-stream mux (skynet forwarding,
	// app-direct dials, visor RPC): open streams, relay legs, and frames that
	// arrived for streams this side does not have.
	VStream []DiagVStreamMux `json:"vstream,omitempty"`
	// Dmsg is per-session ping health plus the relay nominee/backoff state.
	Dmsg *DiagDmsg `json:"dmsg,omitempty"`
	// Transports is per-transport liveness: missed pongs, last packet age,
	// and which route-ID-0 handlers are wired (a missing one means that
	// packet type is dropped by the router — #4725).
	Transports []DiagTransport `json:"transports,omitempty"`
	// TransportEvents is the last transport.TransportEventRingSize opens and
	// closes, oldest first, each close with the reason the code gave. This is
	// where to look when a transport that was in `tp ls` is gone: the visor log
	// ring holds minutes; this holds the events.
	TransportEvents []transport.TransportEvent `json:"transport_events,omitempty"`
	// TransportLastClose is the last close event of each of the last
	// transport.TransportLastCloseMax transports that closed, keyed by
	// transport id. TransportEvents is a ring and on a public visor it is
	// flooded by autoconnect opens — 256 entries spanned three minutes on one
	// exit visor — so the one close being chased was already gone. This
	// survives any amount of open churn: look up the transport id here to get
	// the reason it died and how old it was.
	TransportLastClose map[uuid.UUID]transport.TransportEvent `json:"transport_last_close,omitempty"`
	// MuxEvents is the last router.MuxEventRingSize route-group and mux-leg
	// changes, oldest first, each with the reason the code gave and who
	// initiated it (local/remote/operator/adaptive/policy). This is where to
	// look when a leg that was in `proxy mux info` is gone and
	// transport_events shows its transport still open — the removal came from
	// the router, not the transport layer.
	MuxEvents []router.MuxEvent `json:"mux_events,omitempty"`
}

DiagSnapshot is the `diag` section of a StateSnapshot.

type DiagTransport

type DiagTransport struct {
	ID          uuid.UUID     `json:"id"`
	Remote      cipher.PubKey `json:"remote"`
	Type        string        `json:"type"`
	MissedPongs int64         `json:"missed_pongs"`
	PongSeen    bool          `json:"pong_seen"`
	// LastRecvAgoS is seconds since the read loop last received any packet
	// (-1 = never). A transport with pongs flowing but climbing app-level
	// timeouts is the router-intake case, not a link problem.
	LastRecvAgoS float64 `json:"last_recv_ago_s"`
	// MalformedFrames counts frames whose type byte was outside the known
	// range — the peer wrote a packet whose size field did not match it.
	MalformedFrames int64    `json:"malformed_frames"`
	Handlers        []string `json:"handlers,omitempty"`
}

DiagTransport is one transport's liveness and wiring.

type DiagVStreamMux

type DiagVStreamMux struct {
	Name string `json:"name"`
	transport.VStreamMuxStats
	StreamList []transport.VStreamInfo `json:"stream_list,omitempty"`
}

DiagVStreamMux names one VStream mux and carries its counters, plus one row per live stream. The rows are what turn "streams: 27" into an answer: which app (or none) opened each one, over which transport, how long ago, and how deep its inbound queue is right now.

type Diagnostics

type Diagnostics interface {
	DialPing(config PingConfig) error
	Ping(config PingConfig) ([]time.Duration, error)
	PingOnce(config PingConfig) (time.Duration, error)
	StopPing(pk cipher.PubKey) error
	StopAllPings() (int, []string, error)
	DialDmsgPing(pk cipher.PubKey) error
	DialDmsgPingViaServer(pk cipher.PubKey, serverPK cipher.PubKey) error
	DmsgPing(conf PingConfig) ([]time.Duration, error)
	DmsgPingOnce(conf PingConfig) (time.Duration, error)
	StopDmsgPing(pk cipher.PubKey) error
	GetDmsgPingServerPK(pk cipher.PubKey) (cipher.PubKey, error)
	GetRemoteDmsgServers(pk cipher.PubKey) ([]cipher.PubKey, error)
	GetPreferredDmsgServer(remotePK cipher.PubKey) (cipher.PubKey, error)
	BandwidthTest(conf BandwidthTestConfig) (BandwidthResult, error)
	DmsgBandwidthTest(conf BandwidthTestConfig) (BandwidthResult, error)
	TestVisor(config PingConfig) ([]TestResult, error)
	DmsgProbe(pk cipher.PubKey, port uint16) (bool, error)
	DmsgProbeReason(pk cipher.PubKey, port uint16) (bool, string, error)
	DmsgProbeViaServer(pk cipher.PubKey, port uint16, serverPK cipher.PubKey) (bool, error)
	SkynetProbe(pk cipher.PubKey, port uint16) (bool, error)
}

Diagnostics is pings, bandwidth tests and probes.

type DialDmsgPingViaServerIn

type DialDmsgPingViaServerIn struct {
	PK       cipher.PubKey
	ServerPK cipher.PubKey
}

type Dmsg

type Dmsg interface {
	DmsgPortHits() []dmsg.PortHit
	DmsgPtyExec(args DmsgPtyExecArgs) (*pty.CommandExecResult, error)
	IsDMSGClientReady() (bool, error)
	DMSGServers() ([]DMSGServerInfo, error)
	// LAN DMSG server
	SetLANDmsgServer(LANDmsgServerInfo) error
	DialDmsgRPC(pk cipher.PubKey) (net.Conn, error)
	DmsgHTTP(req DmsgHTTPRequest) (*DmsgHTTPResponse, error)
	SkynetHTTP(req SkynetHTTPRequest) (*SkynetHTTPResponse, error)
	VisorSCP(req VisorSCPRequest) error
	VisorCat(req VisorCatRequest) (*VisorCatResponse, error)
	DmsgConnectAll() (*DmsgConnectAllResult, error)
	SetDmsgSessionsCount(count int) (*DmsgConnectAllResult, error)
	DmsgSessions() (*DmsgClientSessions, error)
	DmsgConverge(carriers []string) (*DmsgConvergeResult, error)
	// DMSG diagnostics
	DmsgPorterStats() (*DmsgPorterStatus, error)
	DmsgPorterReset() (*DmsgPorterStatus, error)
	DmsgPorterDiag() (*netutil.EphemeralDiagResult, error)
	DmsgReconnect() (int, error)
	DmsgSetMinSessions(n int) error
}

Dmsg is the visor's dmsg client and the requests it makes over dmsg and skynet.

type DmsgClientSessionInfo

type DmsgClientSessionInfo struct {
	PK      cipher.PubKey   `json:"pk"`
	Role    string          `json:"role"` // "main" | "route_setup" | "transport_setup"
	Count   int             `json:"count"`
	Servers []cipher.PubKey `json:"servers"`
	// Sessions carries the same servers plus the protocol each one was reached
	// over (tcp / ws / wss / webtransport / quic). Parallel to Servers, sorted
	// the same way, so existing Servers consumers keep working.
	Sessions []DmsgServerSession `json:"sessions,omitempty"`
	// RelayPeers are the visor's current relay nominees (peers it wants a
	// skynet-carried session through); RelayClients the peers currently
	// attached to THIS visor's relay acceptor.
	RelayPeers   []cipher.PubKey `json:"relay_peers,omitempty"`
	RelayClients []cipher.PubKey `json:"relay_clients,omitempty"`
}

DmsgClientSessionInfo is one dmsg client's current session state.

type DmsgClientSessions

type DmsgClientSessions struct {
	Main           *DmsgClientSessionInfo `json:"main,omitempty"`
	RouteSetup     *DmsgClientSessionInfo `json:"route_setup,omitempty"`
	TransportSetup *DmsgClientSessionInfo `json:"transport_setup,omitempty"`
}

DmsgClientSessions enumerates every dmsg client running inside the visor (main + embedded route setup node + embedded transport setup node) along with the dmsg server PKs each one currently has an active session to. Used by the `skywire cli dmsg sessions` command for operators investigating connectivity issues — the embedded RSN / TPS use SEPARATE dmsg keys and therefore SEPARATE sets of sessions from the main visor, so checking one doesn't tell you about the others.

type DmsgConnectAllResult

type DmsgConnectAllResult struct {
	Total            int               `json:"total"`             // total servers in discovery
	AlreadyConnected int               `json:"already_connected"` // sessions in place before the call
	NewlyConnected   int               `json:"newly_connected"`   // sessions opened by the call
	Failed           map[string]string `json:"failed,omitempty"`  // server PK → error text for any that could not be connected
}

DmsgConnectAllResult summarizes the result of DmsgConnectAll.

type DmsgConvergeResult

type DmsgConvergeResult struct {
	Carriers  []string              `json:"carriers"`  // the effective ordered preference used
	Converged int                   `json:"converged"` // sessions re-dialed to a more-preferred carrier
	Sessions  []dmsg.SessionCarrier `json:"sessions"`  // per-session carrier after the pass
}

DmsgConvergeResult reports the outcome of a carrier-convergence pass.

type DmsgHTTPRequest

type DmsgHTTPRequest struct {
	URL    string            `json:"url"`
	Method string            `json:"method"`
	Header map[string]string `json:"header,omitempty"`
	Body   []byte            `json:"body,omitempty"`
}

DmsgHTTPRequest represents an HTTP request to be made over dmsg

type DmsgHTTPResponse

type DmsgHTTPResponse struct {
	StatusCode int               `json:"status_code"`
	Status     string            `json:"status"`
	Header     map[string]string `json:"header,omitempty"`
	Body       []byte            `json:"body,omitempty"`
}

DmsgHTTPResponse represents an HTTP response received over dmsg

type DmsgPorterStatus

type DmsgPorterStatus struct {
	MainPorts int `json:"main_ports"`
	RSNPorts  int `json:"rsn_ports,omitempty"`
	MainFreed int `json:"main_freed,omitempty"`
	RSNFreed  int `json:"rsn_freed,omitempty"`
}

DmsgPorterStatus contains DMSG porter diagnostics.

type DmsgProbeReasonResponse

type DmsgProbeReasonResponse struct {
	Reachable bool
	Reason    string
}

DmsgProbeReasonResponse carries a probe result together with why it failed.

type DmsgProbeRequest

type DmsgProbeRequest struct {
	PK   cipher.PubKey
	Port uint16
}

type DmsgProbeViaServerRequest

type DmsgProbeViaServerRequest struct {
	PK       cipher.PubKey
	Port     uint16
	ServerPK cipher.PubKey
}

DmsgProbeViaServerRequest is the argument for the DmsgProbeViaServer RPC.

type DmsgPtyExecArgs

type DmsgPtyExecArgs struct {
	RemotePK   cipher.PubKey
	RemotePort uint16
	Req        pty.CommandExecReq
	// Scheme: "" (default — MultiDialer chain), "dmsg" (force dmsg
	// only), or "skynet" (force skynet only). Unknown values
	// return a clear error rather than silently falling back.
	Scheme string
}

DmsgPtyExecArgs is the request shape for API.DmsgPtyExec. RemotePK identifies the target visor's dmsgpty host; RemotePort defaults to pty.DefaultPort (22) when zero. Req carries the command, arguments, optional environment overrides, optional stdin, and per-call timeout — see pty.CommandExecReq.

Scheme overrides the dialer choice. The visor's dmsgpty Host is wired with a MultiDialer that tries skynet first (transport-aware, rides existing transports for low latency) and falls back to dmsg on miss. That ordering breaks down when skynet's dial blocks past the caller's ctx without honoring it — dmsg never gets tried and the whole exec hangs. Operators who know the peer has a working dmsg session can pass Scheme="dmsg" to skip skynet entirely. Empty keeps the default MultiDialer behavior.

type DmsgRelayAttachment

type DmsgRelayAttachment struct {
	PK        cipher.PubKey `json:"pk"`
	Carrier   string        `json:"carrier"`
	Streams   int           `json:"streams"`
	LatencyMS float64       `json:"latency_ms"`
}

DmsgRelayAttachment is one nominated relay this visor holds a session with.

type DmsgRelayClient

type DmsgRelayClient struct {
	PK      cipher.PubKey `json:"pk"`
	Streams int           `json:"streams"`
}

DmsgRelayClient is a peer attached to this visor as its dmsg relay.

type DmsgRelayRole

type DmsgRelayRole struct {
	// Port is the dmsg port the relay acceptor listens on over skynet.
	Port uint16 `json:"relay_port"`
	// RelayPeers are the peers this visor NOMINATED as its relays
	// (relayNominees → dmsg.Client.SetRelayPeers). Same field name and
	// meaning as `dmsg sessions`.
	RelayPeers []cipher.PubKey `json:"relay_peers,omitempty"`
	// Attached is the subset of RelayPeers this visor currently holds a
	// session with, and the carrier that session rides. Carrier "skynet" is
	// an actual relay attachment; anything else means the nominee was reached
	// as a plain dmsg server instead.
	Attached []DmsgRelayAttachment `json:"attached,omitempty"`
	// RelayClients are the peers attached TO this visor's relay acceptor,
	// each with the streams open on its relay session. Same field name and
	// meaning as `dmsg sessions`, which reports the keys alone.
	RelayClients []DmsgRelayClient `json:"relay_clients,omitempty"`
	// RelayedStreams is the relay slots in use across all attached peers, and
	// MaxRelayedStreams the cap they are charged against (dmsg.relay_max_streams).
	// A cap of 0 or less means this visor relays for nobody.
	RelayedStreams    int `json:"relayed_streams"`
	MaxRelayedStreams int `json:"max_relayed_streams"`
	// RelayRefused counts stream requests turned away at capacity (dmsg error
	// 308) since start. Nonzero means this hub refused traffic it was asked to
	// carry — at the dialer that refusal is indistinguishable from the
	// destination being down, so without this it is only ever diagnosed from
	// the wrong end.
	RelayRefused int `json:"relay_refused"`
}

DmsgRelayRole is both directions of the dmsg relay: the relays this visor attaches to, and the peers it relays for.

type DmsgServerClient

type DmsgServerClient struct {
	PK      cipher.PubKey `json:"pk"`
	Streams int           `json:"streams"`
	Peer    bool          `json:"peer,omitempty"`
}

DmsgServerClient is one session held by this visor's in-process dmsg server: the connected key, how many streams it has open, and whether the session is another dmsg server (a peer link) rather than a client.

type DmsgServerRole

type DmsgServerRole struct {
	// Enabled is what the config asks for; Running is what actually started.
	// Enabled && !Running is the interesting case — e.g. no dmsg discovery PK
	// to register with, so the server no-opped at init.
	Enabled bool `json:"enabled"`
	Running bool `json:"running"`
	// Mode distinguishes the two servers a visor can host: "own_key" is the
	// bare server sharing the visor's PK/SK and dmsg client, "config_path" is
	// the whole standalone dmsg-server service run in-process from its own
	// config file, on its OWN key. Empty when no server is configured.
	Mode string `json:"mode,omitempty"`
	// PK is the key the server registers under: the visor's own key in
	// "own_key" mode, the config file's key in "config_path" mode.
	PK cipher.PubKey `json:"pk,omitempty"`
	// OwnKey is true when the server shares the visor's identity, i.e. one
	// discovery entry carries both the Client and the Server section.
	OwnKey bool `json:"own_key"`
	// SharedTransportPort: the server took the dmsg branch of the transport
	// port's TCP multiplexer instead of opening a listener of its own, so no
	// second port is bound or forwarded.
	SharedTransportPort bool   `json:"shared_transport_port"`
	LocalAddress        string `json:"local_address,omitempty"`
	PublicAddress       string `json:"public_address,omitempty"`
	ConfigPath          string `json:"config_path,omitempty"`
	// StartedAt is when the server was started (zero when it is not running).
	StartedAt time.Time `json:"started_at,omitempty"`
	// SessionCount is how many dmsg sessions this server currently holds, and
	// Clients names them. Answering "who is actually on this dmsg server" used
	// to require asking the discovery — which reports what CLIENTS claim about
	// their delegated servers, not what the server itself is holding, and says
	// nothing about how much traffic each one is running. A co-resident visor
	// knows the real answer; this reports it.
	//
	// Peers are separated from ordinary clients because a server-to-server link
	// is not a client at all: counting the two together makes a server look
	// busier than it is, and the peer mesh is the part an operator tunes
	// separately.
	//
	// Only populated in own_key mode — config_path mode runs the server inside
	// an unexported service type that exposes no session handle.
	SessionCount int                `json:"session_count"`
	PeerCount    int                `json:"peer_count"`
	Clients      []DmsgServerClient `json:"clients,omitempty"`
}

DmsgServerRole is the in-process dmsg server (dmsg.server.* in the config).

type DmsgServerSession

type DmsgServerSession struct {
	PK cipher.PubKey `json:"pk"`
	// Carrier is the raw dmsg carrier: tcp | ws | wt | quic.
	Carrier string `json:"carrier"`
	// Protocol is a human-readable label distinguishing ws from wss:
	// tcp | ws | wss | webtransport | quic.
	Protocol string `json:"protocol"`
	// Address is the endpoint the carrier dialed (host:port or ws(s)://…).
	Address string `json:"address,omitempty"`
	// Streams is the number of open mux streams on this session (0 = idle,
	// -1 = unmeasurable/quic). Same count the idle-session reaper uses.
	Streams int `json:"streams"`
}

DmsgServerSession is one active dmsg-server session and the protocol the local client used to reach it.

type EffectiveRoutingConfig

type EffectiveRoutingConfig struct {
	MinHops                 uint16   `json:"min_hops"`
	ForceLocalRoutes        bool     `json:"force_local_routes"`
	ExistingTPOnly          bool     `json:"existing_tp_only"`
	TransportPreference     []string `json:"transport_preference,omitempty"`
	EnableCascadeRouteSetup bool     `json:"enable_cascade_route_setup"`
	PolicyPerDial           string   `json:"policy_per_dial,omitempty"`
}

EffectiveRoutingConfig is the routing configuration in force at runtime. MinHops/MuxRoutes/ForceLocalRoutes/ExistingTPOnly/ TransportPreference are read from the live router (GetRouterSettings), so they reflect any runtime set that diverged from the config file; EnableCascadeRouteSetup and PolicyPerDial are the configured source (PolicyPerDial is a path/inline-source string, never a secret).

type EmbeddedProxiesStatus

type EmbeddedProxiesStatus struct {
	DmsgWeb       *EmbeddedProxyInfo   `json:"dmsg_web,omitempty"`
	SkynetWeb     *EmbeddedProxyInfo   `json:"skynet_web,omitempty"`
	SkymailBridge *EmbeddedSkymailInfo `json:"skymail_bridge,omitempty"`
}

EmbeddedProxiesStatus bundles the state of every in-process resolving proxy. Separate fields (not a map) so the UI can treat each proxy's presence/absence as a hard-coded toggle.

type EmbeddedProxyInfo

type EmbeddedProxyInfo struct {
	// Enabled is the config flag value, reflecting the intended
	// state. Running is the observed state; mismatches happen
	// briefly during Start/Stop or when a dependency is still
	// bootstrapping.
	Enabled bool `json:"enabled"`
	// Running is true once the resolver goroutine has been spawned.
	Running bool `json:"running"`
	// DomainSuffix is the TLD matched by the resolver (e.g. ".dmsg").
	DomainSuffix string `json:"domain_suffix,omitempty"`
	// SocksAddr is the localhost SOCKS5 listener (e.g. "127.0.0.1:4445").
	// Empty when disabled or when Config.ProxyPort is 0.
	SocksAddr string `json:"socks_addr,omitempty"`
	// UpstreamSOCKS is the configured fallthrough, empty for direct.
	UpstreamSOCKS string `json:"upstream_socks,omitempty"`
	// UpstreamRules are the per-domain upstreams, longest suffix first.
	UpstreamRules []proxyroute.Rule `json:"upstream_rules,omitempty"`
	// Stats is the cumulative request counter snapshot. Zero-valued
	// when the resolver has never been constructed.
	Stats *EmbeddedProxyStats `json:"stats,omitempty"`
}

EmbeddedProxyInfo describes the state of one in-process resolving proxy (dmsgweb or skynetweb). The hypervisor UI renders this so users can copy the SOCKS5 address into their browser without poking at the visor config directly.

type EmbeddedProxyStats

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

EmbeddedProxyStats is a JSON-friendly stats snapshot common to dmsgweb and skynetweb. Mirrors pkg/dmsgweb.StatsSnapshot / pkg/skynetweb.StatsSnapshot shapes — duplicated here so the RPC surface stays decoupled from the internal stats types.

type EmbeddedServiceState added in v1.3.98

type EmbeddedServiceState struct {
	Type string `json:"type"`
	Name string `json:"name"`
	// URL is where the service answers over dmsg: the visor's key, its
	// dmsg HTTP port and the service's path prefix.
	URL string `json:"url"`
	// PlainHTTP is the block's plain-HTTP address, if it serves one.
	PlainHTTP string `json:"plain_http,omitempty"`
	Running   bool   `json:"running"`
	Error     string `json:"error,omitempty"`
	services.State
}

EmbeddedServiceState is one deployment service running inside the visor (config.embedded_services), as `skywire cli visor state` reports it.

type EmbeddedSkymailInfo

type EmbeddedSkymailInfo struct {
	Enabled bool   `json:"enabled"`
	Running bool   `json:"running"`
	Addr    string `json:"addr,omitempty"`
	Mode    string `json:"mode,omitempty"`
	Suffix  string `json:"suffix,omitempty"`
}

EmbeddedSkymailInfo is the runtime snapshot for the in-process SMTP bridge. Distinct from EmbeddedProxyInfo (which is SOCKS5- shaped) because the bridge has no upstream-SOCKS concept and its listener is an SMTP server, not a SOCKS5 proxy.

type ExecWasmInfo

type ExecWasmInfo struct {
	// Present is false for a plain source build, which embeds only the
	// placeholder README and falls back to an on-disk module.
	Present bool `json:"present"`
	// Revision is the commit the module was built from, recorded beside it
	// by `make embed-exec-wasm`. Empty when the module predates that.
	Revision string `json:"revision,omitempty"`
	// BinaryRevision is this visor's own commit, for comparison.
	BinaryRevision string `json:"binary_revision,omitempty"`
	// Stale is true when both revisions are known and differ: the desk is
	// serving older code than the visor. Fix with `make embed-exec-wasm`.
	Stale bool `json:"stale"`
	// Stamp is the served content fingerprint, which the page polls.
	Stamp string `json:"stamp,omitempty"`
}

ExecWasmInfo reports the embedded js/wasm command module's provenance.

type FeedStatus

type FeedStatus = cxosub.FeedStatus

FeedStatus is one row of CXOSubscriptionManager.Status.

type FetchCXOArgs

type FetchCXOArgs struct {
	// Feed is one of: "tpd-metrics", "tpd-uptime". (UT's standalone
	// uptime tracker is intentionally absent — that service is being
	// deprecated, so there's no point publishing it over CXO.)
	Feed string `json:"feed"`
	// Path is the TreeStore path inside the feed, e.g.
	// "metrics/days/7" or "uptimes/days/30".
	Path string `json:"path"`
}

FetchCXOArgs identifies which CXO feed + path the caller wants the visor to read from its lazy-on-demand subscriber cache. Feed names are stable strings the CLI's URL→feed mapping table emits; adding a new feed means adding a case to the visor's FetchCXO switch and a row to the mapping table — nothing in between needs to know.

type FetchCXOResult

type FetchCXOResult struct {
	Hit        bool      `json:"hit"`
	Body       []byte    `json:"body,omitempty"`
	LastRootAt time.Time `json:"last_root_at,omitempty"`
	Reason     string    `json:"reason,omitempty"`
}

FetchCXOResult is the visor's reply for a FetchCXO probe. Hit is true when the subscriber returned a cached payload; a miss carries a Reason string ("not ready", "cooling down", "unknown feed", …) so the CLI's debug log has something concrete to print.

type FetchServiceDataIn

type FetchServiceDataIn struct {
	Service string
	Path    string
}

type FilterServersIn

type FilterServersIn struct {
	Version string
	Country string
}

type ForwardedPort

type ForwardedPort struct {
	// Port is the DMSG/skynet port exposed to the network.
	Port int `json:"port"`
	// LocalPort is the TCP port on localhost to forward to. If zero,
	// defaults to Port (same port number locally and remotely).
	LocalPort     int             `json:"local_port,omitempty"`
	Label         string          `json:"label,omitempty"`
	Description   string          `json:"description,omitempty"`
	ShowOnLanding bool            `json:"show_on_landing"`
	Whitelist     []cipher.PubKey `json:"whitelist,omitempty"` // empty = accessible to all authenticated peers
	Skynet        bool            `json:"skynet"`              // forward over skynet (sky-forwarding server)
	DMSG          bool            `json:"dmsg"`                // forward over DMSG (service registry)
	// UDP, when true, enables UDP-datagram semantics on this
	// forward rather than the default TCP-stream semantics.
	// Implementation rides DatagramRouteGroup (see
	// pkg/router/datagram_route_group.go and the faithful-UDP
	// design in #2607): faithful loss, no head-of-line blocking on
	// reorder, per-datagram AEAD. For DNS / NTP / VoIP / gaming —
	// anything where a late packet is worse than a lost one.
	//
	// Stage 4 of #2607: this field is recognized by the visor's
	// forwarded-port listener loop, which binds a local UDP socket
	// at EffectiveLocalPort() and pumps datagrams to/from the
	// peer-side DatagramRouteGroup. The route-setup machinery that
	// constructs the DatagramRouteGroup itself lives in stage 5's
	// app API.
	UDP bool `json:"udp,omitempty"`
	// ProxyAddr is an optional local address (e.g., "127.0.0.1:3000")
	// to reverse-proxy to. For port 80, this replaces the visor's
	// default landing page with content from the local service.
	ProxyAddr string `json:"proxy_addr,omitempty"`
	// PreserveHost controls the Host header on the request the
	// reverse-proxy emits to the backend. Currently effective on the
	// port-80 HTTP reverse-proxy (the only forward type that touches
	// HTTP headers; raw-TCP forwards don't see HTTP at all).
	//
	//   false (default): visor rewrites Host to the backend's address
	//                    (target.Host). Useful when the backend
	//                    validates Host against its listening address
	//                    — the historical behavior.
	//   true:            visor preserves whatever Host the incoming
	//                    request already carried (e.g. magnetosphere
	//                    .net after the skynetweb resolver's
	//                    subdomain rewrite). Required when the
	//                    backend (Caddy, nginx, traefik) dispatches
	//                    its virtual hosts by Host header.
	PreserveHost bool `json:"preserve_host,omitempty"`
	// InjectPK, when true, makes this forward HTTP-aware: instead of a
	// raw TCP splice, the visor terminates HTTP and reverse-proxies to
	// the backend, stamping the noise-authenticated caller into request
	// headers the backend can trust:
	//
	//   X-Skywire-Remote-PK   the caller's 66-hex public key (omitted if
	//                         the peer can't be identified)
	//   X-Skywire-Transport   "dmsg" or "skynet"
	//
	// Any client-supplied copies of those headers are stripped first, so
	// they cannot be spoofed over this path. This lets a forwarded website
	// do per-PK behavior (auth, personalization) that a raw splice can't,
	// because the backend otherwise never learns who is connecting.
	//
	// HTTP only — enabling it on a non-HTTP service breaks the stream. The
	// header is only trustworthy if the backend is reachable ONLY through
	// the visor (bind it to loopback); see docs/guides/skynet-website-auth.md.
	InjectPK bool `json:"inject_pk,omitempty"`
}

ForwardedPort describes a single forwarded port with its metadata.

func (*ForwardedPort) DialTarget

func (fp *ForwardedPort) DialTarget() string

DialTarget returns the address that incoming forwarded connections should be proxied to. ProxyAddr wins when set ("host:port" or "ip:port"); otherwise it falls back to localhost:EffectiveLocalPort.

func (*ForwardedPort) EffectiveLocalPort

func (fp *ForwardedPort) EffectiveLocalPort() int

EffectiveLocalPort returns the local TCP port to forward to. Returns LocalPort if set, otherwise Port.

type Forwarding

type Forwarding interface {
	RegisterTCPPort(localPort int) error
	DeregisterTCPPort(localPort int) error
	ListTCPPorts() ([]int, error)
	RegisterForwardedPort(p ForwardedPort) error
	UpdateForwardedPort(p ForwardedPort) error
	ListForwardedPorts() ([]ForwardedPort, error)
	// DialUDPForward / StopUDPForward / ListUDPForwards drive client-side
	// faithful-UDP port forwarding (#2607): bridge a local UDP socket to
	// a remote forwarded_ports.udp service via DialPacket.
	DialUDPForward(remotePK cipher.PubKey, remotePort, localPort int) error
	StopUDPForward(localPort int) error
	ListUDPForwards() ([]int, error)
	ConnectRawTCP(network string, remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error)
	DisconnectRawTCP(id uuid.UUID) error
	ListRawTCP() (map[uuid.UUID]*appnet.RawTCPForwardConn, error)
}

Forwarding is exposing local ports over skynet and dmsg, and forwarding remote ones.

type GroupAddMemberRequest

type GroupAddMemberRequest struct {
	ID    string        `json:"id"`
	NewPK cipher.PubKey `json:"new_pk"`
}

GroupAddMemberRequest is the input to RPC.GroupAddMember.

type GroupCatalogEntry

type GroupCatalogEntry struct {
	ID        string                  `json:"id"`
	HostPK    cipher.PubKey           `json:"host_pk"`
	Name      string                  `json:"name"`
	Kind      skychatgroup.Kind       `json:"kind"`
	Mode      skychatgroup.Mode       `json:"mode"`
	Policy    skychatgroup.JoinPolicy `json:"policy"`
	PriceHint string                  `json:"price_hint,omitempty"`
	Address   string                  `json:"address"`
}

GroupCatalogEntry is the RPC-facing shape of one discovered group.

Port and PoWBits are deliberately absent: they are join mechanics the visor uses internally, and a caller acts on Address, which carries everything needed to join and nothing a UI has to understand.

type GroupCatalogResponse

type GroupCatalogResponse struct {
	Entries   []GroupCatalogEntry `json:"entries"`
	Truncated bool                `json:"truncated,omitempty"`
}

GroupCatalogResponse pairs the discovered entries with whether the host had more than it sent.

type GroupCreateArgs

type GroupCreateArgs struct {
	Name string `json:"name"`

	// Kind is the group type: "public" (open admission, plaintext),
	// "private" (admin-approved admission, encrypted) or "channel"
	// (open admission, plaintext, only admins may post). Preferred
	// over Mode.
	Kind skychatgroup.Kind `json:"kind,omitempty"`

	// Mode is the legacy field, kept so an older RPC client keeps
	// working: it carried the same two values and mapped onto the same
	// two group types. Used only when Kind is empty, and it cannot name
	// a channel — public and channel share ModePublic.
	Mode skychatgroup.Mode `json:"mode,omitempty"`

	InitialMembers []cipher.PubKey `json:"initial_members,omitempty"`

	// DisablePeerBackfill is the creator's choice on whether any online
	// member may serve this group's history to a joiner, or only admins.
	// The zero value keeps backfill ENABLED, which is the default an
	// operator gets by not saying anything and matches the group record's
	// own inverted field. An admin can change it later with
	// GroupSetPeerBackfill.
	DisablePeerBackfill bool `json:"disable_peer_backfill,omitempty"`

	// Listed opts the new group into this visor's discovery catalog. The
	// zero value keeps it unlisted, which is the private direction — see
	// skychatgroup.Record.Listed for why that default matters.
	Listed bool `json:"listed,omitempty"`
}

GroupCreateArgs is the RPC input for GroupCreate.

func (GroupCreateArgs) ResolvedKind

func (a GroupCreateArgs) ResolvedKind() skychatgroup.Kind

ResolvedKind resolves the group type from either field, defaulting to public.

type GroupCreateResponse

type GroupCreateResponse struct {
	Info   GroupInfo `json:"info"`
	Invite string    `json:"invite"`
}

GroupCreateResponse pairs the persisted GroupInfo with the freshly-encoded invite link the operator should distribute.

type GroupDescriptor

type GroupDescriptor struct {
	ID        string                  `json:"id"`
	HostPK    cipher.PubKey           `json:"host_pk"`
	Name      string                  `json:"name"`
	Kind      skychatgroup.Kind       `json:"kind"`
	Mode      skychatgroup.Mode       `json:"mode"`
	Policy    skychatgroup.JoinPolicy `json:"policy"`
	Port      uint16                  `json:"port"`
	PoWBits   uint8                   `json:"pow_bits,omitempty"`
	ReadOnly  bool                    `json:"read_only,omitempty"`
	PriceHint string                  `json:"price_hint,omitempty"`
	Banned    bool                    `json:"banned,omitempty"`
}

GroupDescriptor is the RPC-facing shape of group.GroupDescriptor. Re-declared rather than aliased so the RPC surface can carry display-oriented additions (a rendered kind label, later a price) that the protocol type has no business knowing about.

type GroupFileKeyArgs

type GroupFileKeyArgs struct {
	ID     string `json:"id"`
	FileID string `json:"file_id"`
}

GroupFileKeyArgs is the RPC input for GroupFileKey: which group, and which attachment within it.

type GroupFileKeyResult

type GroupFileKeyResult struct {
	// Seal is the key to encrypt a new attachment with. Nil for a
	// plaintext (public) group, where sealing would protect nothing: the
	// caller sends such files as-is.
	Seal []byte `json:"seal,omitempty"`

	// Open is every key that may open an existing attachment, current
	// epoch first then the ring — the order that makes the common case a
	// single trial decryption.
	Open [][]byte `json:"open,omitempty"`

	// Epoch is the group's current key generation, for diagnostics.
	Epoch uint64 `json:"epoch,omitempty"`

	// Encrypted reports whether this group seals attachments at all,
	// distinguishing "public group, nothing to do" from "encrypted group
	// whose key we somehow lack" — which is an error, not a no-op.
	Encrypted bool `json:"encrypted"`
}

GroupFileKeyResult carries the keys for one group attachment.

Two fields rather than one because the two uses differ: sealing needs exactly the current key, while opening has to try the retired ones too (an attachment shared before a rotation must keep opening afterwards, the same way message history does).

Both are keys DERIVED for this one file id — the group key itself never crosses the RPC boundary. See the group package's filekey.go.

type GroupHistoryPageArgs

type GroupHistoryPageArgs struct {
	GroupID string `json:"group_id"`
	// Before is the exclusive upper bound — the oldest message the caller
	// already has. Zero means "the newest page".
	Before time.Time `json:"before,omitempty"`
	Limit  int       `json:"limit,omitempty"`
}

GroupHistoryPageArgs is the RPC input for GroupHistoryPage.

type GroupHistoryRequest

type GroupHistoryRequest struct {
	GroupID string `json:"group_id"`
	Limit   int    `json:"limit"`
}

GroupHistoryRequest is the input shape for GroupHistory. GroupID is required; Limit caps the result set (0 = all).

type GroupInfo

type GroupInfo struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	// Avatar is the group's picture as a data URI, empty when unset.
	// The URI form rather than bytes for the same reason Profile carries
	// one over RPC: JSON-friendly and directly usable as an <img> src,
	// with the MIME inside it always the validated one.
	Avatar  string            `json:"avatar,omitempty"`
	OwnerPK cipher.PubKey     `json:"owner_pk"`
	Port    uint16            `json:"port"`
	Mode    skychatgroup.Mode `json:"mode"`
	// Kind is the user-facing group type — "public" (open admission,
	// plaintext) or "private" (admin-approved admission, encrypted).
	// Mode is retained above for older clients; new code should read
	// Kind, JoinPolicy and PostPolicy.
	Kind skychatgroup.Kind `json:"kind,omitempty"`
	// JoinPolicy and PostPolicy are derived, not stored: they answer
	// "can someone walk in" and "who may speak right now" so a UI
	// doesn't have to re-derive the rules from Kind + ReadOnly.
	JoinPolicy skychatgroup.JoinPolicy `json:"join_policy,omitempty"`
	PostPolicy skychatgroup.PostPolicy `json:"post_policy,omitempty"`

	// Banned and Muted are the moderation lists. Banned PKs are out of
	// the group and refused at the join gate; muted PKs read but
	// cannot post.
	Banned []cipher.PubKey `json:"banned,omitempty"`
	Muted  []cipher.PubKey `json:"muted,omitempty"`

	// ReadOnly suspends posting for every non-admin.
	ReadOnly bool `json:"read_only,omitempty"`

	// JoinPoWBits is how much proof of work this group asks of a join
	// request, in leading zero bits — the price of one identity. Zero
	// means none. Surfaced so an operator under a flood can see the
	// current setting and raise it.
	JoinPoWBits uint8 `json:"join_pow_bits"`

	// PeerBackfill reports whether any online member may serve this
	// group's history and messages to a joiner (true) or only admins
	// (false). Positive sense here even though the record stores the
	// negative — an API consumer should not have to reason about a double
	// negative to render a checkbox.
	PeerBackfill bool `json:"peer_backfill"`

	// Listed reports whether THIS visor publishes the group in its
	// discovery catalog. Local, not gossiped — another admin's copy of the
	// same group carries its own answer. See skychatgroup.Record.Listed.
	Listed bool `json:"listed,omitempty"`

	// KeyEpoch is the generation of the key this group currently encrypts
	// with — 0 for a plaintext group or one still on its create-time key,
	// incremented by every rotation. Surfaced so an operator can see that
	// an eviction actually re-keyed the group, and that every member
	// converged onto the same epoch.
	KeyEpoch uint64 `json:"key_epoch,omitempty"`

	// KeyAge is how long the current key has been in force, in seconds.
	// The epoch number alone doesn't say whether it is a week old or a
	// year; this is what makes "bounded in time" visible.
	KeyAgeSeconds int64 `json:"key_age_seconds,omitempty"`

	// KeyRotatesInSeconds is how long until this group's key is due for
	// replacement on age. Negative means it is already due and the next
	// background tick will re-key it. Zero for a plaintext group.
	KeyRotatesInSeconds int64 `json:"key_rotates_in_seconds,omitempty"`

	// GovernanceSealed reports whether this group's membership and
	// moderation history is encrypted on the feed as well as its
	// messages — who was added, promoted, banned or muted, and when.
	//
	// True exactly when the group is encrypted; surfaced as its own
	// field rather than left for a client to infer from Kind, because
	// "the bans are private too" is a distinct promise from "the
	// messages are private" and it was NOT true before this build. A
	// client that shows it can tell the operator which one they have.
	GovernanceSealed bool `json:"governance_sealed,omitempty"`

	// CanPost / CannotPostReason answer "may THIS visor post into this
	// group right now", pre-computed so the UI can disable the composer
	// and explain why without duplicating the precedence rules
	// (banned > muted > read-only).
	CanPost          bool   `json:"can_post"`
	CannotPostReason string `json:"cannot_post_reason,omitempty"`

	// PendingJoins is the number of join requests awaiting an admin
	// decision. Zero for non-admins and for open groups, which admit
	// without queuing. Populated by GroupList and GroupGet.
	PendingJoins int `json:"pending_joins,omitempty"`
	// Admins is the per-record roster-authority set. Mirrors
	// Record.Admins exactly — founder is implicitly admin and is
	// surfaced explicitly here after EnsureFounderInAdmins runs on
	// the store read path, so RPC clients can render the admin list
	// without having to OR in OwnerPK themselves.
	Admins        []cipher.PubKey     `json:"admins,omitempty"`
	Members       []cipher.PubKey     `json:"members"`
	Role          skychatgroup.Role   `json:"role"`
	Status        skychatgroup.Status `json:"status"`
	CreatedAt     time.Time           `json:"created_at"`
	JoinedAt      time.Time           `json:"joined_at"`
	LastMessageAt time.Time           `json:"last_message_at,omitempty"`
	// SubscriberAlive is the live health of this visor's subscriber
	// side. Populated by GroupList; left zero by other accessors that
	// don't need it. See group.Manager.IsSubscriberAlive for semantics.
	SubscriberAlive bool `json:"subscriber_alive"`

	// PeerLastInbound is the per-peer last-inbound time, keyed by
	// peer-PK hex. Populated by GroupList and GroupGet alongside
	// SubscriberAlive. A zero time means the peerSub for that peer
	// is connecting, silent since startup, or (legacy s.sub path)
	// not individually tracked. Empty map for owner-role sessions
	// that don't follow peer feeds. Operator-facing: lets `cli
	// skychat group info` show which specific peer is stale in a
	// group whose session-level SubscriberAlive is otherwise true.
	PeerLastInbound map[string]time.Time `json:"peer_last_inbound,omitempty"`

	// SubDropCount is the running total of inbox-to-stream drops
	// across every live + torn-down gRPC StreamGroupMessages
	// subscriber on THIS VISOR. Bumped in groupInbox.deliver's
	// select+default branch when a subscriber's bounded channel is
	// full (per-subscriber 256-message buffer set by the gRPC
	// handler); persisted across unsubscribe so a single rolling
	// total survives stream restarts.
	//
	// Inbox-wide, not group-scoped — the inbox fans every message
	// to every live subscriber regardless of group, so a drop is
	// "this subscriber couldn't keep up" rather than "this group is
	// noisy". Repeated across every GroupInfo here for operator
	// convenience; cross-checking the value between two groups in
	// the same /status response always returns the same number.
	//
	// Distinct from the inbox ring's overflow drop semantics — the
	// ring keeps the most-recent groupInboxCap (1024) messages
	// regardless of subscriber draining. SubDropCount only counts
	// messages that reached deliver's fan-out but failed to enqueue
	// onto a particular subscriber's channel; the ring still has
	// them and the next StreamGroupMessages reconnect's backlog
	// replay picks them up via SinceTimestampNs (#2630/#2659).
	//
	// Surfaced live so the chat-app's /status can flag a busy group
	// whose subscriber side is silently dropping under burst — pre-
	// fix the count was only readable at unsubscribe time, leaving a
	// gap where an operator watching /status saw subscriber_alive=true
	// and last_message_at advancing but the CLI listener pipe
	// missing messages.
	SubDropCount uint64 `json:"sub_drop_count"`

	// DeliverCount is the running total of groupInbox.deliver()
	// invocations since visor startup — every message that landed
	// at the inbox layer, regardless of downstream consumption.
	//
	// Combined with PeerUpdateCount (per-peer) and SubDropCount,
	// localizes drops along the receive path:
	//   PeerUpdateCount[peer]  =  CXO peerSub → Session callback fired
	//   DeliverCount           =  Session → inbox.deliver() enqueued
	//   SubDropCount           =  inbox → gRPC subscriber channel dropped
	//
	// Deltas tell where messages are lost:
	//   peerUpdate_sum < deliverCount  → impossible (deliver is downstream)
	//   peerUpdate_sum > deliverCount  → drops in Session.makePeerOnUpdate's
	//                                    deliver call site (per-peer callback fired
	//                                    but message never reached inbox)
	//   deliverCount > rcvd_at_client  → drops between inbox and the CLI listener
	//                                    (subDropCount accounts for some; the rest
	//                                    are gRPC adapter or stream.Send)
	//
	// Visor-wide, not group-scoped (matches SubDropCount semantics).
	// Repeated across every GroupInfo for operator convenience.
	DeliverCount uint64 `json:"deliver_count"`

	// StreamSendCount is the running total of successful stream.Send
	// calls on rpcgrpc.StreamGroupMessages across both PingServer
	// instances (local CLI + dmsg-RPC). Counts data events only;
	// the Subscribed sentinel that the handler emits before entering
	// the dispatch loop is NOT included.
	//
	// Pairs with DeliverCount to localize drops between the inbox
	// layer and the CLI listener:
	//   DeliverCount    > StreamSendCount  → drops between inbox and
	//                                        stream (SubDropCount accounts
	//                                        for some; rest are adapter
	//                                        backpressure or stream
	//                                        flow-control on the gRPC
	//                                        send buffer).
	//   DeliverCount   ==  StreamSendCount → everything that landed
	//                                        in the inbox was sent to
	//                                        the wire; any operator-
	//                                        observed loss is downstream
	//                                        of stream.Send (gRPC client
	//                                        recv, CLI buffering, or
	//                                        Monitor task wrapper).
	//
	// Visor-wide, not group-scoped. Reset only on visor restart.
	StreamSendCount uint64 `json:"stream_send_count"`

	// PeerUpdateCount is the per-peer count of treestore subscriber
	// OnUpdate callbacks observed by Session.makePeerOnUpdate, keyed
	// by peer-PK hex. Populated alongside PeerLastInbound. Bumped on
	// every events>0 callback (the same seam that bumps the
	// peerLastInboundNs liveness signal).
	//
	// Empty map for owner-role sessions that don't follow peer feeds.
	// Operator-facing: an idle peer with PeerLastInbound zero AND
	// PeerUpdateCount zero has never been heard from; an idle peer
	// with PeerLastInbound stale but PeerUpdateCount > 0 went silent
	// after a known interaction. Comparing PeerUpdateCount sums to
	// DeliverCount localizes drops upstream vs downstream of the
	// inbox enqueue.
	PeerUpdateCount map[string]uint64 `json:"peer_update_count,omitempty"`
}

GroupInfo is the public summary of a chat group, returned by GroupList and GroupGet.

type GroupJoinArgs

type GroupJoinArgs struct {
	// Invite is a skychat:invite:<base64url> link. Self-contained: it
	// works even while the group's host is offline, because it carries
	// the port and mode inline.
	Invite string `json:"invite"`

	// Address is the short form — skychat://<host-pk>/<group-id>. Not
	// self-contained: the host is asked what the group is (see
	// group.Manager.JoinByAddress) before the ordinary join runs, so
	// this needs the host reachable and an invite does not.
	//
	// Both are accepted because they trade against each other rather
	// than one superseding the other: an address is short enough to scan
	// or read aloud and stays correct as the group changes, a link keeps
	// working when nobody is home.
	Address string `json:"address,omitempty"`
}

GroupJoinArgs is the RPC input for GroupJoin. Exactly one of the two fields is used, Invite first.

type GroupJoinPoWRequest

type GroupJoinPoWRequest struct {
	ID   string `json:"id"`
	Bits uint8  `json:"bits"`
}

GroupJoinPoWRequest sets the join proof-of-work difficulty.

type GroupJoinRequest

type GroupJoinRequest struct {
	GroupID   string                  `json:"group_id"`
	PK        cipher.PubKey           `json:"pk"`
	Note      string                  `json:"note,omitempty"`
	AskedAt   time.Time               `json:"asked_at"`
	Status    skychatgroup.JoinStatus `json:"status"`
	DecidedAt time.Time               `json:"decided_at,omitempty"`
	DecidedBy cipher.PubKey           `json:"decided_by,omitempty"`
}

GroupJoinRequest is one entry in a group's admission queue.

type GroupMessage

type GroupMessage struct {
	GroupID  string        `json:"group_id"`
	SenderPK cipher.PubKey `json:"sender_pk"`
	Text     string        `json:"text"`
	TS       time.Time     `json:"ts"`
}

GroupMessage is one inbound message delivered through the visor's group inbox. Outbound (owner-only Sends) are NOT echoed here; the caller already knows what it sent.

type GroupPeerBackfillRequest

type GroupPeerBackfillRequest struct {
	ID      string `json:"id"`
	Enabled bool   `json:"enabled"`
}

GroupPeerBackfillRequest toggles whether any online member may serve the group's history to a joiner.

type GroupPeerRequest

type GroupPeerRequest struct {
	ID string        `json:"id"`
	PK cipher.PubKey `json:"pk"`
}

GroupPeerRequest is the (group, peer) input shared by every admission + moderation command: approve, deny, remove, ban, unban, mute, unmute. One request type rather than seven identical ones — the method name already carries the verb.

type GroupPollRequest

type GroupPollRequest struct {
	Since time.Time `json:"since"`
}

GroupPollRequest is the input to RPC.GroupPoll.

type GroupPromoteAdminRequest

type GroupPromoteAdminRequest struct {
	ID string        `json:"id"`
	PK cipher.PubKey `json:"pk"`
}

GroupPromoteAdminRequest is the input to RPC.GroupPromoteAdmin / GroupDemoteAdmin. Shape mirrors GroupAddMemberRequest.

type GroupReadOnlyRequest

type GroupReadOnlyRequest struct {
	ID       string `json:"id"`
	ReadOnly bool   `json:"read_only"`
}

GroupReadOnlyRequest toggles group-wide read-only.

type GroupResolveArgs

type GroupResolveArgs struct {
	// Address is any spelling the address parser accepts — a bare public
	// key, skychat://<pk>, skychat://<pk>/<group-id> — or a
	// skychat:invite: link, which is answered from the link itself
	// without a round trip.
	Address string `json:"address"`
}

GroupResolveArgs is the RPC input for GroupResolve.

type GroupResolveResult

type GroupResolveResult struct {
	// Target is "dm" or "group".
	Target string `json:"target"`

	// PK is the peer for a DM, the host for a group address.
	PK cipher.PubKey `json:"pk"`

	// Address is the canonical skychat:// form of what was resolved,
	// suitable for display, copying, and QR encoding.
	Address string `json:"address"`

	// Group is populated for a group address: what the group is and what
	// joining it will involve. Nil for a DM.
	Group *GroupDescriptor `json:"group,omitempty"`

	// Joined reports that this visor is already an active member, so the
	// caller should open the group rather than offer to join it.
	Joined bool `json:"joined,omitempty"`

	// Status is this visor's own record status for the group when one
	// exists — "awaiting_approval" and "denied" in particular, which are
	// the difference between offering "Send request" and explaining that
	// one was already refused.
	Status skychatgroup.Status `json:"status,omitempty"`

	// Invite carries the link back when the input WAS a link, so a
	// caller that resolved one can hand it straight to GroupJoin without
	// re-parsing.
	Invite string `json:"invite,omitempty"`
}

GroupResolveResult describes what an address points at, so a UI can offer the one right action instead of making the user pick.

The zero-ish DM case is deliberately cheap: a bare public key needs no network at all to classify, because a key that names a person and a key that hosts a channel are the same 66 characters — only the presence of a group ID distinguishes them, and that is in the address.

type GroupSendArgs

type GroupSendArgs struct {
	ID   string `json:"id"`
	Text string `json:"text"`
}

GroupSendArgs is the RPC input for GroupSend.

type GroupSetListedRequest

type GroupSetListedRequest struct {
	ID     string `json:"id"`
	Listed bool   `json:"listed"`
}

GroupSetListedRequest is the input to RPC.GroupSetListed.

type GroupSetMetaArgs

type GroupSetMetaArgs struct {
	ID string `json:"id"`

	// Name is the new display name; empty keeps the current one.
	Name string `json:"name,omitempty"`

	// Avatar is the new picture as a data URI (or bare base64), consulted
	// only when SetAvatar is true. SetAvatar with an empty Avatar clears
	// the picture — the flag is what tells "clear it" apart from "leave
	// it alone".
	Avatar    string `json:"avatar,omitempty"`
	SetAvatar bool   `json:"set_avatar,omitempty"`
}

GroupSetMetaArgs is the input for GroupSetMeta. Zero-valued halves stay untouched, so one call can rename, re-picture, or both.

type GroupUnsendArgs

type GroupUnsendArgs struct {
	ID string `json:"id"`
	TS int64  `json:"ts"`
}

GroupUnsendArgs is the RPC input for GroupUnsend. TS is the message's UnixNano timestamp (the value carried in the delivered message).

type HVAddTransportArgs

type HVAddTransportArgs struct {
	PK      cipher.PubKey
	Remote  cipher.PubKey
	TpType  string
	Label   string
	Timeout time.Duration
}

type HVAppArgs

type HVAppArgs struct {
	PK      cipher.PubKey
	AppName string
}

type HVAutoconnectArgs

type HVAutoconnectArgs struct {
	PK     cipher.PubKey
	Enable bool
}

type HVAutostartArgs

type HVAutostartArgs struct {
	PK        cipher.PubKey
	AppName   string
	Autostart bool
}

type HVCalcRoutesArgs

type HVCalcRoutesArgs struct {
	PK     cipher.PubKey
	Enable bool
}

type HVDmsgSessionsArgs

type HVDmsgSessionsArgs struct {
	PK    cipher.PubKey
	Count int
}

type HVForwardedPortArgs

type HVForwardedPortArgs struct {
	PK   cipher.PubKey
	Port ForwardedPort
}

type HVLogsArgs

type HVLogsArgs struct {
	PK      cipher.PubKey
	Since   time.Time
	AppName string
}

type HVMinHopsArgs

type HVMinHopsArgs struct {
	PK   cipher.PubKey
	Hops uint16
}

type HVProxyArgs

type HVProxyArgs struct {
	PK     cipher.PubKey
	Kind   string // "dmsg" or "skynet"
	Enable bool
	Addr   string // SOCKS5 upstream
}

type HVRewardArgs

type HVRewardArgs struct {
	PK   cipher.PubKey
	Addr string
}

type HVRoutingRuleArgs

type HVRoutingRuleArgs struct {
	PK  cipher.PubKey
	Key routing.RouteID
}

type HVTCPPortArgs

type HVTCPPortArgs struct {
	PK   cipher.PubKey
	Port int
}

type HVTransportArgs

type HVTransportArgs struct {
	PK  cipher.PubKey
	TID uuid.UUID
}

type HVVisorEntry

type HVVisorEntry struct {
	PK             cipher.PubKey `json:"pk"`
	Online         bool          `json:"online"`
	IsLocal        bool          `json:"is_local,omitempty"`
	Version        string        `json:"version,omitempty"`
	BuildTag       string        `json:"build_tag,omitempty"`
	Uptime         float64       `json:"uptime_seconds,omitempty"`
	LocalIP        string        `json:"local_ip,omitempty"`
	PublicIP       string        `json:"public_ip,omitempty"`
	CountryCode    string        `json:"country_code,omitempty"`
	IsSymmetricNAT bool          `json:"symmetric_nat,omitempty"`
	Transports     int           `json:"transports"`
	// TransportSummaries is the full per-transport detail (type,
	// remote PK, sent/recv counters, etc.) the hvui's node-list
	// table needs to render its Transports column. Populated by
	// populateEntryFromSummary from summary.Overview.Transports.
	// omitempty so older sub-hypervisor binaries that don't fill
	// this field don't blow the JSON shape.
	TransportSummaries []*TransportSummary `json:"transport_summaries,omitempty"`
	Apps               int                 `json:"apps"`
	RewardAddress      string              `json:"reward_address,omitempty"`
	ConfigVersion      string              `json:"config_version,omitempty"`
	// ServicesHealth is the remote visor's most recent
	// HealthInfo.ServicesHealth ("healthy", "unhealthy",
	// "connecting", ""). Carried so the hvui's tree-summary
	// sub-section can render the same green / yellow / gray status
	// dot that the local section's main table does — without this,
	// every sub-section row falls through to nodeStatusClass's
	// "online but health unknown" branch and renders as a gray
	// outline circle even when the visor is fully healthy.
	ServicesHealth string `json:"services_health,omitempty"`
	// Hostname is the visor's os.Hostname(). The hvui's main node
	// list uses this as the default Label when no explicit label is
	// set; without it, sub-section rows render with an empty Label
	// column even though the visor itself reports a valid hostname.
	// Carried separately from PK because Overview.Hostname doesn't
	// survive the HVVisorEntry round-trip otherwise.
	Hostname string `json:"hostname,omitempty"`
	Error    string `json:"error,omitempty"`
	// ProxiedVia is set when this entry was discovered through a connected
	// sub-hypervisor rather than a direct connection. The value is the PK
	// of the sub-hypervisor that proxies operations on this visor.
	ProxiedVia *cipher.PubKey `json:"proxied_via,omitempty"`
	// Load is the visor's resource snapshot (load average, mem %, disk %),
	// from summary.Load. Rendered by `hv ls --load`. omitempty so older
	// sub-hypervisor binaries that don't carry it don't break the JSON shape.
	Load *LoadStats `json:"load,omitempty"`
	// Hypervisors is the set of hypervisors THIS visor is configured to be
	// managed by (its conf.Hypervisors), reported by the visor itself. An
	// attached visor commonly answers to more than one hypervisor, and until
	// now the only way to learn the others was to ask that visor directly:
	// the tree sections describe hypervisors THIS one can reach, not the ones
	// its peers have set. omitempty so an older visor that does not report
	// them is absent rather than empty.
	Hypervisors []cipher.PubKey `json:"hypervisors,omitempty"`
}

HVVisorEntry is a summary of a remote visor connected to this hypervisor.

type HVVisorTree

type HVVisorTree struct {
	Sections []HVVisorTreeNode `json:"sections"`
}

HVVisorTree is the structured response of HVListVisorsTree — a flat list of hypervisor sections. The first entry is the local hypervisor; subsequent entries are sub-hypervisors reachable from it. Each section's `via_chain` documents the path from the local hypervisor down to that sub-hypervisor, so the UI can render a breadcrumb without recomputing.

Visor entries replicate across sections: a visor V that's connected to both the local hypervisor and a sub-hypervisor will appear in BOTH sections — the semantics are "every table shows every visor directly connected to that hypervisor," intentionally. Tables themselves dedup: two paths to the same sub-hypervisor render once.

type HVVisorTreeNode

type HVVisorTreeNode struct {
	HypervisorPK cipher.PubKey   `json:"hypervisor_pk"`
	ViaChain     []cipher.PubKey `json:"via_chain,omitempty"`
	Visors       []HVVisorEntry  `json:"visors"`
	SubError     string          `json:"sub_error,omitempty"`
}

HVVisorTreeNode is one hypervisor's section in the tree response: the hypervisor's PK, the chain of hypervisors from the local one down to this one (empty for the local hypervisor itself), and the visors directly connected to this hypervisor (NOT transitively merged from any sub-hypervisors below it).

Sub-hypervisors that fail to respond surface their error via the SubError field instead of being silently dropped — the UI can render a placeholder row with the error text, and operators can tell "no sub-visors" from "query failed."

type HealthInfo

type HealthInfo struct {
	ServicesHealth         string `json:"services_health"`
	UptimeTrackerHealth    string `json:"uptime_tracker_health,omitempty"`
	AutoconnectHealth      string `json:"autoconnect_health,omitempty"`
	TransportabilityHealth string `json:"transportability_health,omitempty"`
}

HealthInfo carries information about visor's services health. ServicesHealth is the aggregate status — "connecting" if any subsystem is unhealthy, "healthy" only when all are healthy. The per-subsystem fields let the UI show which specific subsystem is the blocker rather than a generic label.

type HostStatsInfo

type HostStatsInfo struct {
	// Host identity (mostly static; useful as a header above the
	// graphs so the user knows which box they're looking at).
	Hostname      string `json:"hostname,omitempty"`
	OS            string `json:"os,omitempty"`
	Platform      string `json:"platform,omitempty"`
	Arch          string `json:"arch,omitempty"`
	UptimeSeconds uint64 `json:"uptime_seconds,omitempty"`

	// CPU
	CPUPercent      float64 `json:"cpu_percent"`
	CPUCount        int     `json:"cpu_count"`
	CPULogicalCount int     `json:"cpu_logical_count"`

	// Memory (bytes)
	MemTotal     uint64  `json:"mem_total"`
	MemUsed      uint64  `json:"mem_used"`
	MemAvailable uint64  `json:"mem_available"`
	MemPercent   float64 `json:"mem_percent"`
	SwapTotal    uint64  `json:"swap_total,omitempty"`
	SwapUsed     uint64  `json:"swap_used,omitempty"`
	SwapPercent  float64 `json:"swap_percent,omitempty"`

	// Disk — root filesystem only. Per-mount breakdown can be a
	// follow-up; "/" is the signal users care about for "is the
	// visor about to run out of room for its log buffer / cxo
	// tree-store?"
	DiskTotal   uint64  `json:"disk_total,omitempty"`
	DiskUsed    uint64  `json:"disk_used,omitempty"`
	DiskFree    uint64  `json:"disk_free,omitempty"`
	DiskPercent float64 `json:"disk_percent,omitempty"`

	// Network — cumulative across all interfaces since boot. Client
	// diffs to derive Bps. Per-interface breakdown is omitted to
	// keep the payload small; can be added later as a `Per []…`
	// field if a user wants it.
	NetBytesSent   uint64 `json:"net_bytes_sent"`
	NetBytesRecv   uint64 `json:"net_bytes_recv"`
	NetPacketsSent uint64 `json:"net_packets_sent,omitempty"`
	NetPacketsRecv uint64 `json:"net_packets_recv,omitempty"`

	// Visor process specifics. Process is nil when we can't read
	// our own /proc entry (rare; surface as null in JSON).
	Process *ProcessStatsInfo `json:"process,omitempty"`
}

HostStatsInfo carries a snapshot of host system + visor process resource utilization. Returned by Visor.HostStats and surfaced to the hypervisor UI via /visors/<pk>/host-stats.

All byte counts are bytes (not KB/MB); the UI is responsible for scaling for display.

type HypervisorPasswordChangeIn

type HypervisorPasswordChangeIn struct {
	OldPassword string
	NewPassword string
}

HypervisorPasswordChangeIn carries the old + new password for the hypervisor UI "admin" account. Mirrors the shape of usermanager.ChangePassword and SkychatPasswordChangeIn.

type Hypervisors

type Hypervisors interface {
	RemoteVisors() ([]string, error)
	AddHypervisor(pk cipher.PubKey) error
	// PendingHypervisors lists peers waiting to be approved as hypervisors
	// (a same-origin transport or a refused RPC), by fingerprint.
	PendingHypervisors() ([]PendingHypervisor, error)
	// ApproveHypervisor approves a pending key by public key or fingerprint
	// (or unique prefix); it is AddHypervisor with a lookup in front.
	ApproveHypervisor(sel string) (cipher.PubKey, error)
	// NewPairCode mints a one-time pairing code valid for ttl (0 = default).
	NewPairCode(ttl time.Duration) (PairCode, error)
	RemoveHypervisor(pk cipher.PubKey) error
	RemoveAllHypervisors() (int, error)
	SetHypervisorPassword(oldPassword, newPassword string) error
	SetHypervisorPasswordForce(newPassword string) error
	TransportRPCCall(remotePK cipher.PubKey, method string, args json.RawMessage) (json.RawMessage, error)
	HVListVisors() ([]HVVisorEntry, error)
	HVListDirectVisors() ([]HVVisorEntry, error)
	HVListVisorsTree() (*HVVisorTree, error)
	HVVisorSummary(pk cipher.PubKey) (*Summary, error)
	HVStartApp(pk cipher.PubKey, appName string) error
	HVStopApp(pk cipher.PubKey, appName string) error
	HVSetMinHops(pk cipher.PubKey, hops uint16) error
	HVSetRewardAddress(pk cipher.PubKey, addr string) (string, error)
	HVRemoveTransport(pk cipher.PubKey, tid uuid.UUID) error
	HVRemoveRoutingRule(pk cipher.PubKey, key routing.RouteID) error
	HVAddTransport(pk, remote cipher.PubKey, tpType, label string, timeout time.Duration) (*TransportSummary, error)
	HVSetPublicAutoconnect(pk cipher.PubKey, enable bool) error
	HVSetCalculateRoutes(pk cipher.PubKey, enable bool) error
	HVReload(pk cipher.PubKey) error
	HVShutdown(pk cipher.PubKey) error
	HVServiceHealth(pk cipher.PubKey) ([]ServiceHealthEntry, error)
	HVDmsgSessions(pk cipher.PubKey) (*DmsgClientSessions, error)
	HVDmsgConnectAll(pk cipher.PubKey) (*DmsgConnectAllResult, error)
	HVSetDmsgSessionsCount(pk cipher.PubKey, count int) (*DmsgConnectAllResult, error)
	HVLogsSince(pk cipher.PubKey, since time.Time, appName string) ([]string, error)
	HVSetAutoStart(pk cipher.PubKey, appName string, autostart bool) error
	HVEmbeddedProxies(pk cipher.PubKey) (*EmbeddedProxiesStatus, error)
	HVSetEmbeddedProxyEnabled(pk cipher.PubKey, kind string, enable bool) error
	HVSetEmbeddedProxyUpstream(pk cipher.PubKey, kind, addr string) error
	HVListTCPPorts(pk cipher.PubKey) ([]int, error)
	HVRegisterTCPPort(pk cipher.PubKey, port int) error
	HVDeregisterTCPPort(pk cipher.PubKey, port int) error
	HVListForwardedPorts(pk cipher.PubKey) ([]ForwardedPort, error)
	HVRegisterForwardedPort(pk cipher.PubKey, p ForwardedPort) error
	HVUpdateForwardedPort(pk cipher.PubKey, p ForwardedPort) error
}

Hypervisors is pairing this visor with hypervisors, and a hypervisor's control of the visors paired with it.

type LANDmsgServerInfo

type LANDmsgServerInfo struct {
	Enabled       bool          `json:"enabled"`
	PK            cipher.PubKey `json:"pk,omitempty"`
	Address       string        `json:"address,omitempty"`        // LAN-routable "host:port" advertised by the hypervisor.
	PublicAddress string        `json:"public_address,omitempty"` // Operator-set WAN-routable "host:port"; empty when not configured.
	// DiscoveryURL is the hypervisor-hosted dmsg-discovery proxy URL that
	// receiving visors should use as their primary discovery (with their
	// existing public dmsg-discovery as fall-through behind it). Empty
	// when the hypervisor's HTTP address isn't remotely reachable AND the
	// operator hasn't set lan_dmsg_server.public_discovery_url. Visors
	// that receive a non-empty value save it to config; the change takes
	// effect on the next visor restart.
	DiscoveryURL string `json:"discovery_url,omitempty"`
}

type LoadStats

type LoadStats struct {
	Load1           float64 `json:"load1"`
	Load5           float64 `json:"load5"`
	Load15          float64 `json:"load15"`
	CPUCores        int     `json:"cpu_cores"`
	MemUsedPercent  float64 `json:"mem_used_percent"`
	DiskUsedPercent float64 `json:"disk_used_percent"`
}

LoadStats is a lightweight, always-meaningful resource snapshot used by `cli visor hv ls --load`: load averages (which, unlike a single CPUPercent sample, need no prior baseline to be meaningful) plus instantaneous memory and root-disk usage percentages. Cheap to gather (a few /proc reads + one statfs), so it's populated on every Summary without a sampling interval.

type LocalTransportStatsResponse

type LocalTransportStatsResponse struct {
	// Transports is the per-transport rollup, sorted by total
	// bytes (sent+recv) descending so the busiest transports
	// come first.
	Transports []*stats.TransportRecord `json:"transports"`
	// FetchedAt is when the snapshot was assembled. Lets the
	// hvui surface a "last sample" timestamp.
	FetchedAt time.Time `json:"fetched_at"`
}

LocalTransportStatsResponse is the wire shape returned by LocalTransportStats. Keep this stable independent of the bbolt schema — additive fields only on changes.

type LocalUptimeArgs

type LocalUptimeArgs struct {
	Since time.Time `json:"since,omitempty"`
	Until time.Time `json:"until,omitempty"`
}

LocalUptimeArgs is the request shape. Empty Since/Until mean "default 7-day window ending now" — caller-side defaults match the logserver's own /stats/uptime behavior so a hypervisor and a direct curl agree on the rendered timeline.

type LocalUptimeResponse

type LocalUptimeResponse struct {
	Tiers     map[string]map[string]string `json:"tiers"`
	Since     time.Time                    `json:"since"`
	Until     time.Time                    `json:"until"`
	FetchedAt time.Time                    `json:"fetched_at"`
}

LocalUptimeResponse is the wire shape for LocalUptimeStats.

The tier name is whatever the tracker recorded (the visor uses "process", "dmsg", "skynet"; future probes may add more — keep the renderer name-agnostic). Dates are UTC YYYY-MM-DD; bitmaps are 288 chars where '.' = online slot and ' ' = offline slot.

type Mail

type Mail interface {
	// MailStatus reports whether the mailbox runs, its addresses, and
	// who may deliver to it.
	MailStatus() (*MailStatus, error)
	// MailList lists a folder ("INBOX" or "Sent"), newest first.
	MailList(folder string) ([]skymail.Summary, error)
	// MailRead renders one message and marks it read.
	MailRead(folder, id string) (*skymail.Rendered, error)
	// MailRaw returns one message exactly as stored.
	MailRaw(folder, id string) ([]byte, error)
	// MailAttachment decodes attachment n of a message, numbered as
	// MailRead lists them.
	MailAttachment(folder, id string, n int) (*skymail.AttachmentData, error)
	// MailSend delivers a message now; a recipient whose visor cannot
	// be reached gets an error in the result, not a queue entry.
	MailSend(msg skymail.Outgoing) (*skymail.SendResult, error)
	// MailDelete removes one message.
	MailDelete(folder, id string) error
	// MailSetWhitelist replaces the PKs allowed to deliver; empty
	// accepts everyone. Persisted with the mail.
	MailSetWhitelist(pks []cipher.PubKey) error
	// MailSetSettings changes whether the mailbox runs and its limits,
	// at once, and keeps them beside the mail (settings.json).
	MailSetSettings(u MailSettingsUpdate) error
}

Mail is the visor's own mailbox (pkg/skymail): mail to its PK over skynet and dmsg, kept verbatim in a Maildir.

type MailAttachmentRequest

type MailAttachmentRequest struct {
	Folder string
	ID     string
	N      int
}

MailAttachmentRequest names one attachment of one message.

type MailMessageRequest

type MailMessageRequest struct {
	Folder string
	ID     string
}

MailMessageRequest names one message.

type MailSettingsUpdate

type MailSettingsUpdate struct {
	Enable         *bool          `json:"enable,omitempty"`
	MaxMessageSize *int64         `json:"max_message_size,omitempty"`
	MaxTotalSize   *int64         `json:"max_total_size,omitempty"`
	MaxAge         *time.Duration `json:"max_age,omitempty"`
}

MailSettingsUpdate changes the fields that are set; nil leaves one as it is. A zero limit restores its default, a negative one removes it.

func (MailSettingsUpdate) Wire

Wire encodes u for the RPC.

type MailSettingsWire

type MailSettingsWire struct {
	EnableSet, MaxMessageSizeSet, MaxTotalSizeSet, MaxAgeSet bool

	Enable         bool
	MaxMessageSize int64
	MaxTotalSize   int64
	MaxAge         time.Duration
}

MailSettingsWire is MailSettingsUpdate as it crosses the RPC. gob flattens pointers and drops zero values, so a pointer to false or to 0 would arrive as nil: "leave it" instead of "turn it off" or "back to the default". The Set flags carry the difference.

func (MailSettingsWire) Update

Update decodes w.

type MailStatus

type MailStatus struct {
	Enabled bool   `json:"enabled"`
	Running bool   `json:"running"`
	Reason  string `json:"reason,omitempty"` // why it is not running
	// Limits are the effective ones; Usage is the bytes held now.
	Limits skymail.Limits `json:"limits"`
	Usage  int64          `json:"usage"`
	// Address and AddressDmsg are the default addresses; any local part
	// at the same domain reaches the same mailbox.
	Address     string          `json:"address,omitempty"`
	AddressDmsg string          `json:"address_dmsg,omitempty"`
	Dir         string          `json:"dir,omitempty"`
	Whitelist   []cipher.PubKey `json:"whitelist"`
	Unread      int             `json:"unread"`
	Total       int             `json:"total"`
}

MailStatus is the state of the visor's mailbox.

type ModulePresence

type ModulePresence struct {
	StatsTracker       bool `json:"stats_tracker"`
	UptimeRecorder     bool `json:"uptime_recorder"`
	EmbeddedTPS        bool `json:"embedded_transport_setup"`
	EmbeddedRouteSetup bool `json:"embedded_route_setup"`

	// ExecWasm describes the js/wasm command module this binary serves to
	// browser desks. Queryable rather than log-only on purpose: `go build .`
	// embeds whatever pkg/wasmhv/execwasm/blob already holds instead of
	// rebuilding it, so a current binary can serve a module many commits
	// old, and the only other signal is a startup warning that scrolls past.
	ExecWasm *ExecWasmInfo `json:"exec_wasm,omitempty"`
}

ModulePresence reports which optional subsystems are wired on this visor. A false here means the module was not configured/started (nil), which is often exactly the thing being debugged ("why is there no stats feed?").

type MuxDirectionInput

type MuxDirectionInput struct {
	AppName string
	Mode    string
}

MuxDirectionInput carries the app-scoped manual direction pin for SetMuxDirection: Mode is "auto" (release), "default" or "flipped".

type MuxHopInfo

type MuxHopInfo struct {
	TpID      string  `json:"tp_id"`
	From      string  `json:"from"`
	To        string  `json:"to"`
	TpType    string  `json:"tp_type"`
	LatencyMS float64 `json:"latency_ms,omitempty"`
}

MuxHopInfo is one hop of a mux leg's forward route. From/To are FULL public keys (never truncated). LatencyMS is the hop's transport RTT where known (first hop owned; single-intermediate far hop derived).

type MuxLegInfo

type MuxLegInfo struct {
	Index       int    `json:"index"`
	TransportID string `json:"transport_id"`
	TpType      string `json:"tp_type"`
	RemotePK    string `json:"remote_pk"`
	// Source names where a leg that is not this group's own dial came from —
	// a leg the standby-pool arbiter took ("standby :4, re-homed in place").
	Source string `json:"source,omitempty"`
	// LatencyMS is the FIRST-HOP transport RTT; RouteLatencyMS is the leg's
	// TRUE end-to-end route latency (all hops, from the leg-liveness pong) �
	// on a multihop leg the two differ sharply. Direct is true for a 1-hop
	// route straight to the destination, false for a relayed (multihop) leg.
	LatencyMS      float64 `json:"latency_ms,omitempty"`
	RouteLatencyMS float64 `json:"route_latency_ms,omitempty"`
	// AckDelayMS is the leg's own EWMA send�ack delay; InflightBytes and
	// WindowBytes are its real unacknowledged bytes and the send window they
	// are bounded by (see router.MuxLeg).
	AckDelayMS    float64 `json:"ack_delay_ms"`
	InflightBytes float64 `json:"inflight_bytes"`
	WindowBytes   float64 `json:"window_bytes"`
	Direct        bool    `json:"direct"`
	SentBytes     uint64  `json:"sent_bytes"`
	SentPackets   uint64  `json:"sent_packets"`
	RecvBytes     uint64  `json:"recv_bytes"`
	RecvPackets   uint64  `json:"recv_packets"`
	// PayloadBytes is the UNIQUE in-order payload this leg delivered (each seq
	// counted once, retransmits/duplicates excluded) � so per-leg values sum to
	// the transfer size and cleanly attribute which legs carried a direction's
	// data, unlike RecvBytes which includes retransmit inflation.
	PayloadBytes uint64 `json:"payload_bytes"`
	// DupBytes is inbound DUPLICATE data (seqs already delivered/buffered on
	// arrival) � the peer's spurious-retransmit waste, which rides the fastest
	// leg. RepairBytes is inbound FEC repair frames (deliberate overhead).
	// With PayloadBytes they decompose RecvBytes, so a standby leg showing
	// traffic is attributable from telemetry.
	DupBytes    uint64 `json:"dup_bytes"`
	RepairBytes uint64 `json:"repair_bytes"`
	// Retransmits is SACK retransmit packets carried by this leg (loss
	// signal). Alive/Standby are the leg's gate_state: Alive=false once the
	// transport is closed; Standby=true for a warm standby (rules kept,
	// not sending). Surfaced for the per-leg telemetry harness.
	Retransmits uint64 `json:"retransmits"`
	// GoodputBps is this leg's recent goodput � the EWMA of (sent+recv) bytes
	// per second over the telemetry refresh window (~1s for the status page).
	// The RATE the leg is currently moving, as opposed to the cumulative
	// SentBytes/RecvBytes totals. 0 until a second sample lands.
	// GoodputUpBps/GoodputDownBps split it by direction (send-rate / recv-rate);
	// GoodputBps is their sum.
	GoodputBps     float64 `json:"goodput_bps,omitempty"`
	GoodputUpBps   float64 `json:"goodput_up_bps,omitempty"`
	GoodputDownBps float64 `json:"goodput_down_bps,omitempty"`
	Alive          bool    `json:"alive"`
	Standby        bool    `json:"standby"`
	// Hops is the leg's full forward route (every hop to the destination),
	// with full PKs and per-hop transport type + latency where known.
	Hops []MuxHopInfo `json:"hops,omitempty"`
	// CapacityPriorBps is the pool arbiter's PRIOR throughput estimate for
	// this leg (router.throughputPrior) — what a standby tunnel is ranked by
	// before it has carried anything to measure.
	CapacityPriorBps float64 `json:"capacity_prior_bps,omitempty"`
}

MuxLegInfo is one route in a mux'd group.

type MuxRehomeInput

type MuxRehomeInput struct {
	AppName     string
	TargetPort  uint16
	StandbyPort uint16
}

MuxRehomeInput is the argument of the RehomeTunnelLeg RPC: the app, the ACTIVE tunnel that is to gain a leg, and the STANDBY tunnel whose chain it takes. Both ports are the dst_port `proxy mux info` prints.

type MuxRouteGroupInfo

type MuxRouteGroupInfo struct {
	Desc routing.RouteDescriptorFields `json:"desc"`
	// FarEndPK is the peer at the other end of this route group — the exit for
	// a dialing client. The setup node gives each edge the descriptor that
	// points AT that edge, so Desc.DstPK is the LOCAL visor on both sides and
	// consumers reading it as the destination printed this visor as its own
	// exit. Filled by the router; render this, never an end of Desc.
	FarEndPK cipher.PubKey `json:"far_end_pk"`
	// AppName is the app that dialed this route group. Empty on the accept
	// side, where this visor is the far end of someone else's dial.
	AppName       string `json:"app_name,omitempty"`
	MuxEnabled    bool   `json:"mux_enabled"`
	SACKEnabled   bool   `json:"sack_enabled"`
	PerFrameNoise bool   `json:"per_frame_noise"`
	// Directional: unidirectional send selection (CapUniDir) is active � each
	// direction rides a disjoint leg class. Flipped: the direction->leg-class
	// mapping is swapped (heavy direction took the mux). With per-leg `direct`,
	// this tells which class carries which direction without log-grepping.
	Directional bool `json:"directional,omitempty"`
	Flipped     bool `json:"flipped,omitempty"`
	// FlipPinned is the operator's MANUAL direction pin on a directional group:
	// "auto" (flip controller in charge), "default" or "flipped" (mapping pinned,
	// controller dormant until released). Empty on a non-directional mux.
	FlipPinned string `json:"flip_pinned,omitempty"`
	// Distribution names how the mux spreads outbound packets across its legs
	// (weight mode: "auto", "round-robin", "weighted", "capacity",
	// "latency-adaptive", "sticky:5tuple", "size-threshold", "dscp-priority").
	// Empty for a single-leg / non-mux group.
	Distribution string `json:"distribution,omitempty"`
	// ReorderPending is the number of received packets buffered out-of-order
	// (head-of-line-blocking depth); ReorderGapAgeMS is how long the current
	// reorder frontier gap has stayed open in ms (0 when contiguous). A rising
	// pending count with a growing gap age marks a stalled/black-holing leg.
	ReorderPending  int     `json:"reorder_pending,omitempty"`
	ReorderGapAgeMS float64 `json:"reorder_gap_age_ms,omitempty"`
	// WriteSeq is the total DATA frames this mux has emitted outbound � a cheap
	// aggregate send-progress counter across all legs.
	WriteSeq uint32 `json:"write_seq,omitempty"`
	// AggSentBytes / AggRecvBytes are the rg-scoped totals summed across every
	// leg (what this route group moved, distinct from per-transport totals that
	// also count other groups sharing the transport).
	AggSentBytes uint64 `json:"agg_sent_bytes,omitempty"`
	AggRecvBytes uint64 `json:"agg_recv_bytes,omitempty"`
	// AggGoodputBps is the route-group's recent goodput � the sum of the
	// per-leg goodput RATES (bytes/sec), i.e. what the whole group is moving
	// right now, distinct from the cumulative AggSentBytes/AggRecvBytes totals.
	// AggGoodputUpBps/AggGoodputDownBps split it by direction (sum of the
	// per-leg send-rates / recv-rates); AggGoodputBps is their sum.
	AggGoodputBps     float64 `json:"agg_goodput_bps,omitempty"`
	AggGoodputUpBps   float64 `json:"agg_goodput_up_bps,omitempty"`
	AggGoodputDownBps float64 `json:"agg_goodput_down_bps,omitempty"`
	// FEC (forward error correction) telemetry. FECEnabled reports whether both
	// peers negotiated CapFEC on this group. FECRepairBytesSent/Recv are cumulative
	// repair-frame bytes scheduled onto / received from legs � the TRUE FEC
	// overhead, separable from data (AggRecvBytes) and retransmit (per-leg
	// Retransmits). FECReconstructs counts frontier frames recovered from repair
	// (each a slow-leg head-of-line stall this group avoided).
	FECEnabled         bool         `json:"fec_enabled,omitempty"`
	FECRepairBytesSent uint64       `json:"fec_repair_bytes_sent,omitempty"`
	FECRepairBytesRecv uint64       `json:"fec_repair_bytes_recv,omitempty"`
	FECReconstructs    uint64       `json:"fec_reconstructs,omitempty"`
	Legs               []MuxLegInfo `json:"legs"`
	// Events is this route group's most recent leg/group changes with their
	// reasons, oldest first � the churn next to the legs, so a reading that
	// shows a leg missing also shows who took it and why. The whole-visor ring
	// is diag.mux_events in `visor state`.
	Events []router.MuxEvent `json:"events,omitempty"`
	// Recovery is the group's loss-recovery state: the sender-side retransmit
	// machinery (retx buffer occupancy and held seq range, retransmits sent /
	// skipped-because-no-longer-held / failed, tail-loss probes, inbound SACK
	// feedback) next to the receiver-side reorder frontier (the seq waited on,
	// the packets dammed behind it, outbound SACK feedback, wedge counters). A
	// reorder wedge is a two-ended failure, and before this only the RECEIVER
	// logged anything � the sender had nothing to show. Nil when the group has
	// no mux.
	Recovery *router.MuxRecovery `json:"recovery,omitempty"`
	// TunnelRole is the DIALING end's label for this route group � "active"
	// (it carries streams) or "standby" (held open, kept alive and measured,
	// carrying nothing, ready to take over). Absent for every route group that
	// is not one of a multi-tunnel app's tunnels.
	//
	// Read it on the LOCAL end. An exit sees how many tunnels a client holds
	// but not which of them are in standby, so this field is always empty in
	// an accepting visor's `visor state`.
	TunnelRole string `json:"tunnel_role,omitempty"`
	// DisjointRoutes: routes to the far end sharing no intermediate and no first
	// hop; what an auto-sized standby pool holds (0 = not yet counted).
	DisjointRoutes int `json:"disjoint_routes,omitempty"`
	// LegReserve: a standby group a leg split built — composable as a leg,
	// never promotable to a tunnel (it has no app session).
	LegReserve bool `json:"leg_reserve,omitempty"`
	// AgeMS is how long this route group has existed � for a pooled STANDBY
	// tunnel, its AUDITION age: how long it has been held open and measured
	// without carrying a stream.
	AgeMS float64 `json:"age_ms,omitempty"`
	// Shape is the SESSION's measured multiplexing shape: every ACTIVE tunnel
	// this app holds to this exit with its live leg count — "2x2" is two
	// tunnels of two legs, "4x1" pure stream level, "1x4" pure packet level.
	// ShapeTarget is the shape it is being held at, ShapeSource whether that
	// came from the auto rule (tunnel.count x pool.active_width) or from the
	// mux.shape knob. Advisory: reported, not yet converged toward. Empty on
	// a standby tunnel and on every accept-side group.
	Shape       string `json:"shape,omitempty"`
	ShapeTarget string `json:"shape_target,omitempty"`
	ShapeSource string `json:"shape_source,omitempty"`
	// ShapeTunnels is the k the target asks for (0 under auto); LastMove and
	// MoveCounts are the session's shape-move history, keyed by the four move
	// names pool_leg_taken / pool_leg_released / tunnel_promoted /
	// tunnel_parked.
	ShapeTunnels int                  `json:"shape_tunnels,omitempty"`
	LastMove     *router.MuxShapeMove `json:"last_move,omitempty"`
	MoveCounts   map[string]uint64    `json:"move_counts,omitempty"`
}

MuxRouteGroupInfo is one route-group's mux state plus per-leg counters. Returned by RouteGroupMuxInfo for 'cli proxy mux-info'.

type MuxRouteInput

type MuxRouteInput struct {
	AppName string
	// Forward and Reverse are the caller-supplied hop lists for
	// AddMuxRoute. Same shape 'cli route calc' emits. Empty for
	// RemoveMuxRoute.
	Forward []routing.Hop
	Reverse []routing.Hop
	// TransportID identifies the leg to remove; unused by AddMuxRoute.
	TransportID uuid.UUID
	// Target is the desired total leg count for GrowMuxRoute and MinHops
	// is the hop-count floor for the legs it adds. Both unused by
	// Add/RemoveMuxRoute.
	Target  int
	MinHops int
	// Legs is the RELATIVE leg count for GrowMuxFromPool � "grow this group
	// by k legs" � as opposed to Target's absolute "grow it to N legs".
	// Unused by every other op.
	Legs int
	// SrcPort disambiguates between concurrent rg's owned by the same
	// app (one per concurrent SOCKS5 connection on skysocks-client,
	// etc.). Zero means "auto-pick if exactly one rg is active for
	// the app, error otherwise."
	SrcPort uint16
}

type MuxWeightsInput

type MuxWeightsInput struct {
	AppName string
	RGPort  uint16
	Weights map[string]float64
}

MuxWeightsInput is the argument of the SetMuxWeights / ClearMuxWeights / MuxWeights RPCs. Weights are keyed by the leg's first-hop transport id as a string, so the wire form matches what `mux info` prints and what an operator types.

type NetworkViewResponse

type NetworkViewResponse = netview.Response

type Node

type Node interface {
	Overview() (*Overview, error)
	Summary() (*Summary, error)
	StateSnapshot() (*StateSnapshot, error)
	StateSnapshotProjected(fields []string) (*StateSnapshot, error)
	Health() (*HealthInfo, error)
	IsStartupComplete() bool
	EnableHypervisor() error
	DisableHypervisor() error
	EnableHypervisorPersist(persist bool) error
	DisableHypervisorPersist(persist bool) error
	IsHypervisorEnabled() bool
	EnableHypervisorUIPersist(persist bool) error
	DisableHypervisorUIPersist(persist bool) error
	IsHypervisorUIServing() bool
	SetHypervisorAuthPersist(enable, persist bool) error
	IsHypervisorAuthEnabled() bool
	Uptime() (float64, error)
	UptimeHistory(args UptimeHistoryArgs) (*UptimeHistoryResponse, error)
	RuntimeStats() (*RuntimeStatsInfo, error)
	GoroutineDump() (string, error)
	Reload() error
	Suspend() error
	Resume() error
	IsSuspended() (bool, error)
	Shutdown() error
	RuntimeLogs() (string, error)
	RuntimeLogsSince(since int64) (RuntimeLogsDelta, error)
	HostStats() (*HostStatsInfo, error)
	NetworkView() (*NetworkViewResponse, error)
	Ports() (map[string]PortDetail, error)
	SetRewardAddress(string) (string, error)
	GetRewardAddress() (string, error)
	DeleteRewardAddress() error
	GetRuntimeConfig() ([]byte, error)
	SetRuntimeConfig(rawJSON []byte) error
	SetConfigFields(fields map[string]json.RawMessage) ([]ConfigFieldChange, error)
	// Skyenv reads /etc/skywire.conf: the durable settings every config
	// regen is built from. SetSkyenv edits it the way `skywire autoconfig
	// --<flag>` does; it takes effect at the next regen.
	Skyenv() (SkyenvState, error)
	SetSkyenv(edits SkyenvEdits) (SkyenvState, error)
	GetConfigPath() (string, error)
	ReinitiateModule(module string) error
	DeregisterService(pks []cipher.PubKey, serviceType string) error
	StartUIServer(addr string) error
	StopUIServer() error
	UIServerStatus() (*UIServerStatus, error)
	CheckAREntry(pk string) ([]string, error)
	ARSelfInfo() (*ARSelfRegistration, error)
	// Close closes the API connection (for RPC clients)
	Close() error
}

Node is the visor itself: status, lifecycle, configuration, rewards and the UI server.

type NodeHealth

type NodeHealth struct {
	PK          cipher.PubKey `json:"pk"`
	Healthy     bool          `json:"healthy"`
	LastChecked time.Time     `json:"last_checked"`
	LastError   string        `json:"last_error,omitempty"`
	Latency     time.Duration `json:"latency_ms"`
}

NodeHealth represents the health status of a setup node.

type Overview

type Overview struct {
	PubKey          cipher.PubKey         `json:"local_pk"`
	BuildInfo       *buildinfo.Info       `json:"build_info"`
	AppProtoVersion string                `json:"app_protocol_version"`
	Apps            []*appserver.AppState `json:"apps"`
	Transports      []*TransportSummary   `json:"transports"`
	RoutesCount     int                   `json:"routes_count"`
	LocalIP         string                `json:"local_ip"`
	PublicIP        string                `json:"public_ip"`
	IsSymmetricNAT  bool                  `json:"is_symmetic_nat"`
	// NATType is the visor's STUN-classified NAT type (e.g. "Full cone NAT",
	// "Symmetric NAT"), determined once at startup. Surfaced so `cli visor ip`
	// reports the visor's already-known result instead of re-running STUN.
	NATType             string          `json:"nat_type,omitempty"`
	CountryCode         string          `json:"country_code,omitempty"`
	RegionName          string          `json:"region_name,omitempty"`
	CityName            string          `json:"city_name,omitempty"`
	Latitude            float64         `json:"latitude,omitempty"`
	Longitude           float64         `json:"longitude,omitempty"`
	Hypervisors         []cipher.PubKey `json:"hypervisors"`
	ConnectedHypervisor []cipher.PubKey `json:"connected_hypervisor"`
	// Hostname is the operating-system hostname the visor process
	// sees at the time of the Overview call. Best-effort:
	// os.Hostname() failures (sandbox / containerized environments
	// missing the syscall) yield an empty string rather than
	// erroring out the whole Overview. Surfaced to operators in the
	// hypervisor UI as the default label fallback when no explicit
	// label is set, and shown in `cli visor info`.
	Hostname string `json:"hostname,omitempty"`
}

Overview provides a range of basic information about a Visor.

type PairAddRequest

type PairAddRequest struct {
	PeerPK cipher.PubKey `json:"peer_pk"`
}

PairAddRequest is the input to RPC.PairAdd.

type PairCode

type PairCode struct {
	Code    string    `json:"code"`
	Expires time.Time `json:"expires"`
}

PairCode is a one-time pairing code and when it stops working.

type PairDeleteRequest

type PairDeleteRequest struct {
	PeerPK cipher.PubKey `json:"peer_pk"`
	// ID is the message id returned by PairSend.
	ID string `json:"id"`
}

PairDeleteRequest is the input to RPC.PairDelete.

type PairInfo

type PairInfo struct {
	PeerPK        cipher.PubKey  `json:"peer_pk"`
	Status        pairing.Status `json:"status"`
	Port          uint16         `json:"port"`
	EstablishedAt time.Time      `json:"established_at"`
	LastMessageAt time.Time      `json:"last_message_at,omitempty"`

	// Epoch is the short-lived key this conversation currently seals
	// under, as hex. Empty means the pair has not derived one yet and is
	// still on the legacy static key — which is worth showing, because
	// the two have materially different guarantees and nothing else in
	// the UI would distinguish them.
	Epoch string `json:"epoch,omitempty"`

	// ForwardSecret is true once an epoch exists, i.e. once messages
	// stop being openable with the two visors' identity keys alone.
	ForwardSecret bool `json:"forward_secret"`

	// KeyGeneration is how many ratchet keys this side has minted for
	// the pair. Each one retires the previous secret, so it doubles as
	// "how many times has the window closed behind us".
	KeyGeneration uint64 `json:"key_generation,omitempty"`
}

PairInfo is the public summary of a chat pair, returned by PairList and used as the poll cursor for PairPoll.

type PairMessage

type PairMessage struct {
	PeerPK cipher.PubKey `json:"peer_pk"`
	Text   string        `json:"text"`
	TS     time.Time     `json:"ts"`

	// ID names this message on both sides of the pair. On a chat message
	// it is the message's own id (derived from the sender's timestamp);
	// on a delete record it is the id of the message being retracted.
	// Clients need it to correlate a delete with the bubble it removes.
	ID string `json:"id,omitempty"`

	// Type is empty for a chat message and pairing.MessageTypeDelete for
	// a retraction. A client that only knows about chat messages should
	// skip any record with a non-empty Type rather than render it.
	Type string `json:"type,omitempty"`
}

PairMessage is one inbound message delivered through the visor's pair inbox. Outbound messages (sent via PairSend) are not echoed here; the caller already knows what it sent.

type PairPollRequest

type PairPollRequest struct {
	// Since is the lower bound (exclusive). Pass time.Time{} (zero)
	// to retrieve the entire current inbox window.
	Since time.Time `json:"since"`
}

PairPollRequest is the input to RPC.PairPoll.

type PairSendRequest

type PairSendRequest struct {
	PeerPK cipher.PubKey `json:"peer_pk"`
	Text   string        `json:"text"`
}

PairSendRequest is the input to RPC.PairSend.

type PendingHypervisor

type PendingHypervisor struct {
	PK          cipher.PubKey `json:"pk"`
	Fingerprint string        `json:"fingerprint"`
	FirstSeen   time.Time     `json:"first_seen"`
	LastSeen    time.Time     `json:"last_seen"`
	// Via is how the peer was noticed: "transport" (it holds a same-origin
	// transport to us) or "rpc" (it tried the transport RPC and was refused).
	Via string `json:"via"`
}

PendingHypervisor is a peer that asked to drive this visor but is not yet in its hypervisor list.

type PingConfig

type PingConfig struct {
	PK          cipher.PubKey
	Tries       int
	PcktSize    int
	PubVisCount int
	LocalRoute  bool           // Skip route finder and use local route calculation
	TransportID string         // Optional: use specific transport (skips route calculation)
	ForwardHops []RouteHopInfo // Optional: explicit forward route (skips route calculation)
	ReverseHops []RouteHopInfo // Optional: explicit reverse route (skips route calculation)
	// RouteIndex identifies which of multiple parallel routes to the
	// same peer this PingConfig refers to. Zero (the default) means
	// "primary / single route" and preserves all legacy single-route
	// behavior. Non-zero values are used by mux-aware callers
	// (`cli visor ping mux-bw`) to dial multiple routes to the same
	// target without their conns trampling each other in the visor's
	// per-route conn map. See PingRouteRef in pkg/visor/ping.go.
	RouteIndex int
	// MinHops, when > 0, forces the route-finder to skip paths with
	// fewer than this many hops on THIS dial — overrides the visor-
	// global routing.min_hops config for the duration of the call.
	// Plumbed straight into router.DialOptions.MinHops. Used by
	// `cli visor ping mux-bw --min-hops N` to verify the operator's
	// "mux via intermediates > direct" hypothesis: without it, the
	// router's direct-transport fast path would short-circuit every
	// dial to use the direct stcpr whenever one existed. 0 = inherit
	// visor-global setting.
	MinHops int
	// SetupTimeout, when > 0, overrides DialPing's hardcoded 30s
	// dial timeout. Multi-hop route setup through 4+ intermediates
	// can take 30-120s (route-finder + setup-node round trips +
	// saveRouteGroupRules retries), exceeding the legacy 30s
	// ceiling. mux-bw passes its cfg.SetupTimeout here. 0 falls
	// back to the existing 30s default.
	SetupTimeout time.Duration
	// Timeout bounds a single PingOnce round-trip (ack + echo reads).
	// 0 = 10s default. mux-bw's probe loop sets this to the remaining
	// pump/idle window so a stuck probe can't outlive the measurement
	// and hang the run.
	Timeout time.Duration
}

PingConfig use as configuration for ping command

type PoolTunnelInfo

type PoolTunnelInfo struct {
	// LocalPort is the standby route group's local port (Desc.SrcPort on
	// this end) — the same port the pool arbiter and `mux_route_groups`
	// already key on.
	LocalPort routing.Port `json:"local_port"`
	// FirstHopPK/TpType are the leading leg's remote pk and transport type.
	FirstHopPK string `json:"first_hop_pk,omitempty"`
	TpType     string `json:"tp_type,omitempty"`
	// Hops is the leading leg's forward hop count (1 for a direct route).
	Hops int `json:"hops"`
	// CapacityPriorBps is the leading leg's PRIOR throughput estimate — the
	// value the pool ranks candidates by before it has measured one.
	CapacityPriorBps float64 `json:"capacity_prior_bps,omitempty"`
	// AuditionAgeMS is how long this standby tunnel has been held open,
	// pinged and measured without carrying a stream (MuxRouteGroupInfo.AgeMS).
	AuditionAgeMS float64 `json:"audition_age_ms,omitempty"`
	// LegSource names where the leading leg came from when it is not this
	// group's own dial (e.g. "standby :4, re-homed in place").
	LegSource string `json:"leg_source,omitempty"`
	// Legs is this standby tunnel's total leg count.
	Legs int `json:"legs"`
}

PoolTunnelInfo is one row of the standby-tunnel pool table (`visor state --select pool`). Every field is projected from the leading leg of a MuxRouteGroupInfo entry whose TunnelRole is "standby" — see poolTableFrom.

type PortDetail

type PortDetail struct {
	Port string
	Type string
}

PortDetail type of port details

type ProcessStatsInfo

type ProcessStatsInfo struct {
	PID         int32   `json:"pid"`
	CPUPercent  float64 `json:"cpu_percent"`
	MemRSS      uint64  `json:"mem_rss"`
	MemVMS      uint64  `json:"mem_vms,omitempty"`
	NumThreads  int32   `json:"num_threads,omitempty"`
	NumFDs      int32   `json:"num_fds,omitempty"`
	StartTimeMS int64   `json:"start_time_ms,omitempty"`
	OpenConns   int     `json:"open_conns,omitempty"`
}

ProcessStatsInfo is the visor process's slice of HostStats — what process.NewProcess(os.Getpid()) returns from gopsutil.

type Profile

type Profile struct {
	// PK is whose profile this is. Filled in by the visor from the key it
	// was asked about (or its own), never from the answer's body — a host
	// that could name the key in its own response could name someone
	// else's.
	PK cipher.PubKey `json:"pk"`

	Name string `json:"name,omitempty"`

	// Avatar is the encoded image, bounded by profile.MaxAvatarDim and
	// profile.MaxAvatarBytes on the way in. Carried as a
	// data URI rather than raw bytes because the only consumer is a
	// browser <img>, and building it here keeps the declared MIME type
	// the one the visor actually decoded.
	Avatar string `json:"avatar,omitempty"`

	Updated time.Time `json:"updated,omitzero"`

	// Address is the canonical skychat:// form of this key, so a caller
	// holding a profile can render, copy or QR-encode the address without
	// knowing the grammar.
	Address string `json:"address,omitempty"`
}

Profile is the RPC-facing shape of a skychat profile.

Re-declared rather than aliased for the same reason GroupDescriptor is: the RPC surface carries display-oriented additions the protocol type has no business knowing about — here, the public key it belongs to and a ready-to-render data URI, both of which are derived by the receiver.

type ProfileSetArgs

type ProfileSetArgs struct {
	Name string `json:"name"`

	// Avatar is the new image as a data URI (what a browser's canvas
	// produces) or raw base64. Empty clears the avatar; see Clear for
	// removing the profile entirely.
	//
	// A string rather than []byte so the one caller that matters can pass
	// exactly what `canvas.toDataURL()` gave it, with no re-encoding step
	// to get wrong on the way.
	Avatar string `json:"avatar,omitempty"`

	// Clear removes the published profile entirely rather than saving an
	// empty one. Distinct from sending blank fields because "I have no
	// profile" and "my name is empty" want the same disk state and the
	// caller should not have to know that.
	Clear bool `json:"clear,omitempty"`
}

ProfileSetArgs is the RPC input for ProfileSet.

type Proxies

type Proxies interface {
	// EmbeddedProxies reports the runtime state of the in-process
	// .dmsg / .skynet resolving proxies. Hypervisor UI consumes this
	// to render the "browser proxy" widget — listener addresses,
	// domain suffix, running state.
	EmbeddedProxies() (*EmbeddedProxiesStatus, error)
	// SetEmbeddedProxyEnabled flips a resolver on or off at runtime.
	// `kind` is "dmsg" or "skynet"; `enable` true starts the
	// resolver, false stops it. Idempotent. Only affects the live
	// runtime — the on-disk config is unchanged, so a visor restart
	// reverts to the config's Enable flag.
	SetEmbeddedProxyEnabled(kind string, enable bool) error
	SetEmbeddedProxyUpstream(kind, addr string) error
	SetEmbeddedProxyBind(kind, addr string) error
}

Proxies is the visor's embedded proxies.

type ProxyTestConfig

type ProxyTestConfig struct {
	Servers []cipher.PubKey `json:"servers"`  // Proxy servers to test
	TestURL string          `json:"test_url"` // URL to fetch through proxy (default from deployment.Prod.GeoIP)
	Timeout time.Duration   `json:"timeout"`  // Timeout per test (default: 30s)
}

ProxyTestConfig configures proxy testing

type ProxyTestResult

type ProxyTestResult struct {
	PK       string `json:"pk"`       // Proxy server public key
	Status   string `json:"status"`   // "OK", "FAIL", "TIMEOUT"
	Duration int64  `json:"duration"` // Duration in milliseconds
	IP       string `json:"ip"`       // IP returned by test
	Location string `json:"location"` // Geo location (City, Country)
	Error    string `json:"error"`    // Error message if failed
}

ProxyTestResult contains the result of a single proxy test

type RecentAppLogRequest

type RecentAppLogRequest struct {
	// AppName should match the app name in visor config.
	AppName string `json:"app_name"`
	// Level is the minimum severity ("trace"|"debug"|"info"|"warn"|"error").
	// Empty defaults to "debug".
	Level string `json:"level"`
}

RecentAppLogRequest asks for the recent app-scoped route/transport events + log lines captured in the log broadcaster's per-app ring.

type RegisterCXOFeedRequest

type RegisterCXOFeedRequest struct {
	Name        string `json:"name"`
	DmsgPort    uint16 `json:"dmsg_port"`
	Description string `json:"description,omitempty"`
}

RegisterCXOFeedRequest is the input to RPC.RegisterCXOFeed.

type RolesSnapshot

type RolesSnapshot struct {
	// PK is the visor's own public key — the key the own-key dmsg server and
	// the relay acceptor both answer under.
	PK cipher.PubKey `json:"pk"`
	// NoTransit reports routing.no_transit: this visor refuses to be an
	// INTERMEDIATE hop on someone else's route (its own edges still work).
	NoTransit bool `json:"no_transit"`

	DmsgServer DmsgServerRole `json:"dmsg_server"`
	DmsgRelay  DmsgRelayRole  `json:"dmsg_relay"`
}

RolesSnapshot is the `roles` section of a StateSnapshot.

type RouteGroupInfo

type RouteGroupInfo struct {
	ConsumeRuleID routing.RouteID               `json:"consume_rule_id"`
	FwdRuleID     routing.RouteID               `json:"fwd_rule_id"`
	Desc          routing.RouteDescriptorFields `json:"desc"`
	FwdNextTpID   string                        `json:"fwd_next_tp_id,omitempty"`
	// Initiator is true when this visor dialed the remote end of the
	// route, false when the route was accepted from a remote setup-node
	// request. Distinguishes outbound proxy/VPN client connections from
	// inbound listener connections.
	Initiator bool `json:"initiator"`
	// Hops is the stored forward route path (transport IDs, edges, types)
	// for this route group, populated when the route group is active.
	Hops []RouteHopInfo `json:"hops,omitempty"`
}

type RouteHopInfo

type RouteHopInfo struct {
	TpID   string
	From   string
	To     string
	TpType string
}

RouteHopInfo contains detailed information about a single hop in a route.

type RouterDialSettings

type RouterDialSettings struct {
	UnknownLatencyCostMs float64  `json:"unknown_latency_cost_ms"`
	UnknownHopPenaltyMs  float64  `json:"unknown_hop_penalty_ms"`
	TypePriorScale       float64  `json:"type_prior_scale"`
	ThroughputPriorScale float64  `json:"throughput_prior_scale"`
	RouteCandidates      int      `json:"route_candidates"`
	MuxRouteHeadroom     int      `json:"mux_route_headroom"`
	ForegroundMux        int      `json:"foreground_mux"`
	TunnelLegs           int      `json:"tunnel_legs"`
	DeadRouteYoungAgeMs  int64    `json:"dead_route_young_age_ms"`
	WarmPlanTTLMs        int64    `json:"warm_plan_ttl_ms"`
	WarmPlanBucketCap    int      `json:"warm_plan_bucket_cap"`
	PreferPKs            []string `json:"prefer_pks,omitempty"`
	ClearPreferPKs       bool     `json:"clear_prefer_pks,omitempty"`
	// TypePriorScaleSet / ThroughputPriorScaleSet mark the two multipliers as
	// given, since 0 is meaningful for them (it drops the term from the score)
	// and so cannot double as "unset".
	TypePriorScaleSet       bool `json:"type_prior_scale_set,omitempty"`
	ThroughputPriorScaleSet bool `json:"throughput_prior_scale_set,omitempty"`
	// TunnelLegsSet distinguishes "set tunnel legs to 0" (turn the dial-time
	// widening off) from "leave it alone", since 0 is both the default and a
	// meaningful instruction.
	TunnelLegsSet bool `json:"tunnel_legs_set,omitempty"`
}

RouterDialSettings bundles the visor-wide DIAL-TIME router knobs — the route-ranking priors, the candidate counts, the warm-plan cache shape, the dead-route young-death window, the dial-time tunnel width and the operator's prefer-these-peers list.

It is a separate struct from RouterSettings (which carries the live-group knobs) so the two commands stay independently readable and a caller parsing `route settings dial --json` gets one document, not a merged one.

On a SET, a zero field means "leave unchanged" — the same tri-state RouterSettings uses — except PreferPKs, where an explicitly empty (non-nil, length-0) slice clears the list. ClearPreferPKs makes that unambiguous over the wire, where nil and empty do not survive the round trip distinctly.

type RouterKnob

type RouterKnob struct {
	Name string `json:"name"`
	Kind string `json:"kind"`
	// Value is the live value, formatted the way `route settings` takes it back.
	Value string `json:"value"`
	// Default is what the binary compiled with.
	Default string `json:"default"`
	// Set reports an explicit set, as opposed to a value that merely equals the
	// default.
	Set bool `json:"set"`
	// Persisted is what the visor config holds for this knob, empty when nothing
	// is written. A Persisted that differs from Value is a change that will be
	// lost — or gained — at the next restart.
	Persisted string `json:"persisted,omitempty"`
	Doc       string `json:"doc,omitempty"`
}

RouterKnob is one row of RouterSettings.KnobState.

type RouterSettings

type RouterSettings struct {
	ForceLocalRoutes bool   `json:"force_local_routes"`
	ExistingTPOnly   bool   `json:"existing_tp_only"`
	MinHops          uint16 `json:"min_hops"`
	// TransportPreference is the transport-type priority order: which type
	// is tried first when a transport has to be created, and which existing
	// one a route rides when several reach the same peer. GET always answers
	// the full order in effect. On PUT:
	//   omitted / null — leave the order unchanged;
	//   []             — revert to the built-in default;
	//   non-empty      — that order, most-preferred first (unlisted types
	//                    sort after it).
	// Written to routing.transport_preference, so it survives a restart —
	// but not a config regen (no skywire.conf field).
	TransportPreference []string `json:"transport_preference,omitempty"`

	// The mux send-window shape and the adaptive park hold, live from
	// pkg/router/settings.go. Each zero value means "leave unchanged" on PUT,
	// so an older client that does not know a field cannot reset it. GET always
	// answers the values in force.
	//
	// windowRefreshInterval is deliberately absent: it becomes a per-route-group
	// ticker when the group is built, so moving it live is a locking change
	// rather than a knob.
	EcfMaxWindowBytes int64         `json:"ecf_max_window_bytes,omitempty"`
	EcfMinWindowBytes int64         `json:"ecf_min_window_bytes,omitempty"`
	EcfWindowMargin   float64       `json:"ecf_window_margin,omitempty"`
	SendWindowWaitMax time.Duration `json:"send_window_wait_max,omitempty"`
	LegParkMinHold    time.Duration `json:"leg_park_min_hold,omitempty"`

	// The dead-route exclusion window and its ceiling, live from
	// pkg/router/settings.go with the same zero-means-unchanged rule.
	DeadRouteHold    time.Duration `json:"dead_route_hold,omitempty"`
	DeadRouteHoldMax time.Duration `json:"dead_route_hold_max,omitempty"`

	// Shared-bottleneck detection: how many per-leg delay samples a verdict
	// needs, and the minimum spacing between two per-SACK samples for one leg.
	// Same zero-means-unchanged rule.
	SBDMinSamples     int           `json:"sbd_min_samples,omitempty"`
	SBDSampleInterval time.Duration `json:"sbd_sample_interval,omitempty"`

	// The terms of the park TRIAL that qualifies a shared-bottleneck ruling: how
	// long the park is held before the aggregate goodput is re-read, the fraction
	// of goodput it may cost before it is undone, and the first exemption window
	// the vindicated pair earns. Same zero-means-unchanged rule.
	SBDTrialWindow time.Duration `json:"sbd_trial_window,omitempty"`
	SBDTrialLoss   float64       `json:"sbd_trial_loss,omitempty"`
	SBDBackoff     time.Duration `json:"sbd_backoff,omitempty"`

	// SBDMinEvidenceRate is the aggregate delivered-bytes rate (B/s) a group must
	// be carrying before a shared-bottleneck ruling may park one of its legs: no
	// traffic, no ruling. Same zero-means-unchanged rule.
	SBDMinEvidenceRate int64 `json:"sbd_min_evidence_rate,omitempty"`

	// ForwardSpill lets a FORWARD frame leave the confined leg when that leg is at
	// its send window (the pre-fix behavior). Off by default: the writer waits for
	// the window instead, because every live row that spilled an upload across two
	// skewed legs collapsed. Tri-state on PUT like SBDDemote — nil leaves it alone.
	ForwardSpill *bool `json:"forward_spill,omitempty"`

	// ForwardSwitchMargin is how much LOWER a challenger leg must measure before the
	// forward direction moves off the leg it holds (0.2 = 20 %), for two consecutive
	// samples. Same zero-means-unchanged rule.
	ForwardSwitchMargin float64 `json:"forward_switch_margin,omitempty"`

	// LegStarveRatio is how many times the best ready leg's delay basis a leg's
	// own must exceed before the scheduler cuts it to a probe per window, and
	// LegProbeBytes is that probe. Same zero-means-unchanged rule; a very large
	// ratio disables the gate.
	LegStarveRatio float64 `json:"leg_starve_ratio,omitempty"`
	LegProbeBytes  int64   `json:"leg_probe_bytes,omitempty"`

	// SBDDemote gates the DEMOTION half of shared-bottleneck detection: false (the
	// default) records every ruling as an sbd_ruling mux event and parks nothing.
	// Like MuxFEC it is a tri-state on PUT — nil leaves it alone — because a bool
	// has no "zero means unchanged" spelling.
	SBDDemote *bool `json:"sbd_demote,omitempty"`

	// MuxFEC advertises FEC on mux route groups created from now on; unlike
	// the rest it is a tri-state on PUT, see SetRouterSettings.
	MuxFEC *bool `json:"mux_fec,omitempty"`

	// Knobs is the whole catalog as one map of catalog-name to formatted value.
	// On PUT it is applied as a PARTIAL set — a knob the map does not name is
	// left alone — which is what lets a sweep move one value without restating
	// the other eighty. On GET it carries every knob's live value, so a bench
	// runner can save it and restore it verbatim.
	Knobs map[string]string `json:"knobs,omitempty"`

	// KnobApp scopes a PUT's Knobs to the route groups owned by one app rather
	// than the whole visor. Empty is the visor-wide set.
	KnobApp string `json:"knob_app,omitempty"`

	// KnobReset restores every knob to its compiled default and drops every
	// per-app override, before Knobs (if any) is applied. With KnobApp set it
	// drops only that app's overrides.
	KnobReset bool `json:"knob_reset,omitempty"`

	// KnobState is the GET-side detail: every knob with its live value, its
	// compiled default, whether it was explicitly set, and what is written in
	// the config — the persisted-vs-live comparison an operator needs before a
	// restart.
	KnobState []RouterKnob `json:"knob_state,omitempty"`

	// AppKnobs is every app's override map on GET, app name to knob map.
	AppKnobs map[string]map[string]string `json:"app_knobs,omitempty"`
}

RouterSettings bundles the visor-wide router knobs the universal "Router Settings" UI panel surfaces. Mirrors the per-flag CLI surface ('cli proxy start --local-route' etc.) so the UI can achieve parity in one round-trip rather than four separate calls.

type Routing

type Routing interface {
	SetExistingTPOnly(enabled bool) error
	SetForceLocalRoutes(enabled bool) error
	GetRouterSettings() (RouterSettings, error)
	SetRouterSettings(s RouterSettings) error
	SetMuxMode(mode string) error
	SetMuxCap(n int) error
	SetMuxWidth(n int) error
	SetMuxStandby(n int) error
	RoutingRules() ([]routing.Rule, error)
	RoutingRule(key routing.RouteID) (routing.Rule, error)
	SaveRoutingRule(rule routing.Rule) error
	RemoveRoutingRule(key routing.RouteID) error
	RouteGroups() ([]RouteGroupInfo, error)
	RoutingStats() (routing.RoutingTableStats, error)
	// RoutingPolicies returns a snapshot of the currently-installed
	// routing-policy state: the visor-wide default plus any per-app
	// overrides. Empty struct when no policies are configured.
	// Hypervisor UI consumes this to display the policy panel in
	// the routing tab.
	RoutingPolicies() (*RoutingPoliciesSummary, error)
	// RouteGroupMuxInfo returns per-mux-leg byte/packet/latency
	// counters for every active route group tagged with the named
	// app (skysocks-client, vpn-client, etc.). Empty slice when
	// nothing is currently dialed via that app.
	RouteGroupMuxInfo(appName string) ([]MuxRouteGroupInfo, error)
	ActiveRoutes() ([]AppRouteStatus, error)
	AddMuxRoute(appName string, fwd, rev []routing.Hop, srcPort uint16) error
	GrowMuxRoute(appName string, target, minHops int, srcPort uint16) (int, error)
	// GrowMuxFromPool grows the app's tunnel by `legs` additional mux legs,
	// built on the plans of the app's STANDBY tunnels to the same exit
	// (already ranked, first-hop transport already up) and falling back to
	// GrowMuxRoute's route-finder path for whatever the pool cannot cover.
	GrowMuxFromPool(appName string, legs, minHops int, srcPort uint16) (int, error)
	RemoveMuxRoute(appName string, tpID uuid.UUID, srcPort uint16) error
	// SetMuxDirection pins (mode "default"/"flipped") or releases (mode
	// "auto") the unidirectional direction→leg-class mapping on ALL of the
	// app's active directional route groups. The pin is coordinated with the
	// peer over the wire; RouteGroupMuxInfo reports the live state.
	SetMuxDirection(appName, mode string) error
	// AddMuxRouteForward is AddMuxRoute with the reverse direction dropped:
	// the leg adds UPSTREAM send capacity only (`proxy mux add --forward-only`).
	AddMuxRouteForward(appName string, fwd, rev []routing.Hop, srcPort uint16) error
	// SetMuxWeights / ClearMuxWeights / MuxWeights are the operator's per-leg
	// send-weight override on one route group — the reachable form of the
	// router's WeightModeExplicit. Clearing returns the group to ECF.
	SetMuxWeights(appName string, weights map[string]float64, srcPort uint16) (router.MuxWeightsView, error)
	ClearMuxWeights(appName string, srcPort uint16) (router.MuxWeightsView, error)
	MuxWeights(appName string, srcPort uint16) (router.MuxWeightsView, error)
	// RouteGroupMuxNegotiated reports, per route group, the capabilities the
	// two ends negotiated and the send-window shape this end is applying.
	RouteGroupMuxNegotiated(appName string) ([]router.MuxNegotiated, error)
	// RehomeTunnelLeg moves a STANDBY tunnel's whole built chain into an ACTIVE
	// tunnel as one more mux leg, in place, with no setup-node dial
	// (`proxy mux adopt`). Both ports are the dst_port `proxy mux info` prints.
	RehomeTunnelLeg(appName string, targetPort, standbyPort uint16) error
	// GetRouterDialSettings / SetRouterDialSettings are the DIAL-TIME router
	// knobs: ranking priors, candidate counts, warm-plan cache shape, the
	// dead-route young-death window and the prefer-these-peers list.
	GetRouterDialSettings() (RouterDialSettings, error)
	SetRouterDialSettings(RouterDialSettings) error
	ServiceHealth() ([]ServiceHealthEntry, error)
	FetchServiceData(service, path string) ([]byte, error)
	SetMinHops(uint16) error
	GetMinHops() (uint16, error)
	SetCalculateRoutes(enabled bool) error
	GetCalculateRoutes() (bool, error)
	GetRouteSetupNodesSorted() ([]cipher.PubKey, error)
	GetRSNHealth() ([]NodeHealth, error)
	// Embedded Route Setup Node (RSN) stats
	RouteSetupStats() (*setupmetrics.StatsSnapshot, error)
	ResetRouteSetupStats() error
}

Routing is routes, route groups, their multiplexing and the router's settings.

type RoutingPoliciesSummary

type RoutingPoliciesSummary struct {
	Default *RoutingPolicyInfo            `json:"default,omitempty"`
	PerApp  map[string]*RoutingPolicyInfo `json:"per_app"`
}

RoutingPoliciesSummary is the JSON shape returned by /visors/{pk}/routing-policies. Default may be nil when no visor-wide policy is configured; PerApp is empty (not nil) so the JSON encodes as {} rather than null for missing data.

type RoutingPolicyInfo

type RoutingPolicyInfo struct {
	// Source is the path or inline marker the engine was
	// constructed from — file path, "<inline>", or "<noop>".
	Source string `json:"source"`

	// Active reports whether an evaluator is currently loaded
	// (false on a noop / failed-to-load engine).
	Active bool `json:"active"`

	// Backend is "skylark" or "wasm", derived from the source
	// file extension. Inline policies are always skylark.
	// Empty string when Source is "<noop>" / "<inline>" — UI
	// can fall back to Source for display.
	Backend string `json:"backend,omitempty"`
}

RoutingPolicyInfo describes one installed engine for the UI.

type RoutingRuleResp

type RoutingRuleResp struct {
	Key     routing.RouteID      `json:"key"`
	Rule    string               `json:"rule"`
	Summary *routing.RuleSummary `json:"rule_summary,omitempty"`
}

RoutingRuleResp is one routing rule as the summary and the hypervisor API report it: its route ID and a hex or summarized form of the rule.

type RuntimeLogsDelta

type RuntimeLogsDelta struct {
	// Entries is the JSON-encoded log lines (each is one full JSON
	// object, same shape returned by RuntimeLogs's array elements).
	Entries []string `json:"entries"`
	// Latest is the highest log_line currently buffered. Pass it as
	// `since` on the next call to receive only newly-arrived entries.
	Latest int64 `json:"latest"`
	// Dropped tells the caller how many entries they missed since
	// their last cursor (because the buffer wrapped past them).
	// Zero when the caller is keeping up with the poll cadence.
	Dropped int64 `json:"dropped"`
}

RuntimeLogsDelta is the diff-streaming response shape: only the entries newer than the caller's cursor, plus the new cursor and a count of entries the caller missed because they aged out of the ring buffer between calls (0 when keeping up).

type RuntimeStatsInfo

type RuntimeStatsInfo struct {
	NumGoroutine int    `json:"num_goroutine"`
	NumCPU       int    `json:"num_cpu"`
	GOMAXPROCS   int    `json:"gomaxprocs"`
	GoVersion    string `json:"go_version"`
	// Memory stats in bytes
	MemAlloc      uint64 `json:"mem_alloc"`
	MemTotalAlloc uint64 `json:"mem_total_alloc"`
	MemSys        uint64 `json:"mem_sys"`
	MemHeapAlloc  uint64 `json:"mem_heap_alloc"`
	MemHeapSys    uint64 `json:"mem_heap_sys"`
	NumGC         uint32 `json:"num_gc"`
}

RuntimeStatsInfo carries Go runtime statistics for the visor process.

type ServiceHealthEntry

type ServiceHealthEntry struct {
	Name      string `json:"name"`
	URL       string `json:"url"`
	Status    string `json:"status"`
	Version   string `json:"version,omitempty"`
	Error     string `json:"error,omitempty"`
	LatencyMs int64  `json:"latency_ms"`
	Transport string `json:"transport,omitempty"` // "dmsg" or "http"
	// IP is the public ip:port of a DMSG server, sourced from the
	// dmsg-discovery all_servers endpoint (queried over dmsg). Only DMSG
	// servers carry an address; other services leave it empty and the UI
	// renders a hyphen.
	IP string `json:"ip,omitempty"`
}

ServiceHealthEntry represents the health status of a deployment service.

type SetAppAddIn

type SetAppAddIn struct {
	AppName    string
	BinaryName string
}

type SetAppArgsIn

type SetAppArgsIn struct {
	AppName string
	Args    []string
}

SetAppArgsIn replaces the entire Args slice on an app.

type SetAppBoolIn

type SetAppBoolIn struct {
	AppName string
	Val     bool
}

type SetAppEnvBatchIn

type SetAppEnvBatchIn struct {
	AppName string
	Env     map[string]string
}

SetAppEnvBatchIn carries a multi-key environment mutation. Map values are strings (env vars are KEY=string by definition); empty values delete.

type SetAppEnvFullIn

type SetAppEnvFullIn struct {
	AppName string
	Env     []string
}

SetAppEnvFullIn replaces the entire Env slice on an app.

type SetAppEnvIn

type SetAppEnvIn struct {
	AppName string
	Key     string
	Value   string
}

SetAppEnvIn carries a single KEY=value mutation for an app's environment. Empty Value deletes the entry.

type SetAppErrorIn

type SetAppErrorIn struct {
	AppName string
	Err     string
}

type SetAppLauncherModeIn

type SetAppLauncherModeIn struct {
	AppName string
	Mode    string
}

SetAppLauncherModeIn persists the launcher-mode preference. Mode is "" / "internal" / "external".

type SetAppMapIn

type SetAppMapIn struct {
	AppName string
	Val     map[string]any
}

type SetAppNetworkInterfaceIn

type SetAppNetworkInterfaceIn struct {
	AppName string
	NetIfc  string
}

type SetAppPKIn

type SetAppPKIn struct {
	AppName string
	PK      cipher.PubKey
}

type SetAppRoutingPolicyIn

type SetAppRoutingPolicyIn struct {
	AppName string
	Path    string
}

SetAppRoutingPolicyIn carries (app name, policy-path) for the runtime per-app routing-policy swap RPC. Path forms match the config field: "@/path.star", "@/path.wasm", inline-Starlark, or "" / "none" to clear.

type SetAppSettingsIn

type SetAppSettingsIn struct {
	AppName string
	Values  map[string]int64
	// Text carries the LIST knobs, whose payload is a token set rather than an
	// int64. Empty Values AND empty Text is the full reset.
	Text map[string]string
}

SetAppSettingsIn replaces the whole live tuning knob set for an app. An empty Values is a full reset to the compiled defaults.

type SetAppStatusIn

type SetAppStatusIn struct {
	AppName string
	Status  string
}

type SetAppStringIn

type SetAppStringIn struct {
	AppName string
	Val     string
}

type SetAppWhitelistIn

type SetAppWhitelistIn struct {
	AppName   string
	Whitelist string
}

SetAppWhitelistIn carries a comma-separated list of public keys allowed to connect to skysocks / vpn-server. Empty = open to all authenticated peers. Replaces the prior SetAppPasswordIn � those apps no longer accept a passcode flag.

type SetAutoStartIn

type SetAutoStartIn struct {
	AppName   string
	AutoStart bool
}

type SetEmbeddedProxyBindRequest

type SetEmbeddedProxyBindRequest struct {
	Kind string
	Addr string // SOCKS5 bind host, "" = loopback
}

type SetEmbeddedProxyEnabledRequest

type SetEmbeddedProxyEnabledRequest struct {
	Kind   string
	Enable bool
}

type SetEmbeddedProxyUpstreamRequest

type SetEmbeddedProxyUpstreamRequest struct {
	Kind string
	Addr string // upstream SOCKS5 address, "" to clear
}

type SetHypervisorAuthIn

type SetHypervisorAuthIn struct {
	Enable  bool
	Persist bool
}

SetHypervisorAuthIn is the argument of SetHypervisorAuth.

type SetSTCPAddrIn

type SetSTCPAddrIn struct {
	PK   cipher.PubKey
	Addr string
}

type SkychatPasswordChangeIn

type SkychatPasswordChangeIn struct {
	OldPassword string
	NewPassword string
}

SkychatPasswordChangeIn carries the old + new password for the hypervisor's "Skychat password" set/change flow. Mirrors the shape of usermanager.ChangePassword.

type SkyenvEdits

type SkyenvEdits struct {
	// Set maps a flag name (e.g. "ishv", "hvpks") to its value as the flag
	// would take it: "true"/"false", a number, or a comma-separated list.
	Set map[string]string `json:"set,omitempty"`
	// Unset lists flag names whose variable returns to its default.
	Unset []string `json:"unset,omitempty"`
}

SkyenvEdits is one SetSkyenv request, addressed by autoconfig flag name.

type SkyenvState

type SkyenvState struct {
	// Path is the file the visor resolved (SKYENV, else the OS default).
	Path string `json:"path"`
	// Exists is false before the first `skywire autoconfig` run creates it.
	Exists bool `json:"exists"`
	// Writable is whether this visor can edit the file. A visor running as
	// an ordinary user cannot edit a root-owned /etc/skywire.conf; a form
	// should then show the `sudo skywire autoconfig --<flag>` to run instead.
	Writable bool `json:"writable"`
	// Values holds the ACTIVE assignments only; a commented line means the
	// variable is at its default and is absent here.
	Values map[string]string `json:"values"`
	// Flags describes every autoconfig flag and the variable it writes.
	Flags []autoconfigcmd.Flag `json:"flags"`
}

SkyenvState is the visor's /etc/skywire.conf as a settings form needs it.

type SkynetHTTPRequest

type SkynetHTTPRequest struct {
	PK     cipher.PubKey     `json:"pk"`
	Port   uint16            `json:"port"`
	Path   string            `json:"path"`
	Method string            `json:"method"`
	Header map[string]string `json:"header,omitempty"`
	Body   []byte            `json:"body,omitempty"`
}

SkynetHTTPRequest represents an HTTP request to make over skynet.

type SkynetHTTPResponse

type SkynetHTTPResponse struct {
	StatusCode int               `json:"status_code"`
	Status     string            `json:"status"`
	Header     map[string]string `json:"header,omitempty"`
	Body       []byte            `json:"body,omitempty"`
}

SkynetHTTPResponse represents an HTTP response received over skynet.

type StartAppIn

type StartAppIn struct {
	AppName      string
	LauncherMode string // "internal", "external", or "" for default
}

type StartVPNClientIn

type StartVPNClientIn struct {
	PK           cipher.PubKey
	LauncherMode string // "internal", "external", or "" for default
}

type StateFieldSet

type StateFieldSet map[string]bool

StateFieldSet is the parsed --select set. A nil set means "everything in the default full snapshot" (proxy stays opt-in even then). An entry present but unknown is ignored here and surfaced as a Note by the builder.

func NewStateFieldSet

func NewStateFieldSet(fields []string) StateFieldSet

NewStateFieldSet parses the requested field keys. An empty/nil fields slice returns a nil set, i.e. build the full default snapshot.

func (StateFieldSet) Has

func (s StateFieldSet) Has(k string) bool

Has reports whether section k should be built. A nil set (no --select) builds every default section; proxy is never a default section (see wantProxy).

type StateSnapshot

type StateSnapshot struct {
	At        time.Time `json:"at"`
	Suspended bool      `json:"suspended"`

	Summary       *Summary                         `json:"summary,omitempty"`
	Health        *HealthInfo                      `json:"health,omitempty"`
	ServiceHealth []ServiceHealthEntry             `json:"service_health,omitempty"`
	RoutingStats  *routing.RoutingTableStats       `json:"routing_stats,omitempty"`
	RouteGroups   int                              `json:"route_groups"`
	RoutingPolicy *RoutingPoliciesSummary          `json:"routing_policy,omitempty"`
	Apps          []*appserver.AppState            `json:"apps,omitempty"`
	Transports    []*TransportSummary              `json:"transports,omitempty"`
	Persistent    []transport.PersistentTransports `json:"persistent_transports,omitempty"`
	Modules       *ModulePresence                  `json:"modules,omitempty"`

	// RouterConfig is the routing configuration actually IN FORCE at
	// runtime (min_hops / mux_routes / force_local / existing_tp_only
	// / transport_preference are the live router values, which can
	// differ from the config file after a runtime set; cascade +
	// policy_per_dial are the configured source). This is the
	// policy-vs-globals view — what the router will do independent of
	// any per-app routing policy.
	RouterConfig *EffectiveRoutingConfig `json:"router_config,omitempty"`
	// MuxRouteGroups is the per-leg mux shape of EVERY active route
	// group (the same RouteGroupMuxInfo 'mux plot' reads), so the live
	// multipath layout — each leg's transport, type, remote, rtt,
	// bandwidth, and alive/standby gate state — is visible in one
	// snapshot rather than one-app-at-a-time.
	MuxRouteGroups []MuxRouteGroupInfo `json:"mux_route_groups,omitempty"`

	// MuxCounters are the whole-router cumulative tallies (tunnel
	// promotions/flips, leg re-homes sent/received/acked/failed, forward
	// fan-out engage/release) that the bounded mux event ring does not keep
	// once it wraps. Built alongside MuxRouteGroups (--select mux).
	MuxCounters *router.MuxCounters `json:"mux_counters,omitempty"`

	// Pool is the standby-tunnel pool table: one row per route group this
	// visor's local end labeled "standby" (pool_arbiter.go), projected from
	// the SAME MuxRouteGroupInfo entries MuxRouteGroups holds — local port,
	// first-hop pk+transport type, hop count, capacity prior bps, audition
	// age, leg source, and leg count, at a glance across the whole pool.
	Pool []PoolTunnelInfo `json:"pool,omitempty"`

	// CXOFeeds is the live publish-health of each CXO feed (system
	// telemetry/tp-list feed first, then user feeds): dirty state, secs
	// since last OK publish, in-memory leaf/node counts, and any standing
	// publish error with its concrete type + isMissingObject verdict.
	// This is the diagnostic surface for TPD-agreement issues — a frozen
	// stats feed (Frozen=true, a climbing SecsSinceOKPublish, a LastErr)
	// is exactly why TPD under-reports a visor's transports, and it's now
	// a query against the live visor (local or --via dmsg://<pk>).
	CXOFeeds []CXOFeedState `json:"cxo,omitempty"`

	Services []EmbeddedServiceState `json:"services,omitempty"`

	// TPDLeafPub says whether the transport manager holds the CXO
	// transport-list snapshot publisher, and — when it does not — why.
	// Without it transport registration silently falls back to the HTTP
	// re-register path, which an empty .cxo alone does not distinguish
	// from "stats module still starting".
	TPDLeafPub *TPDLeafPublisherState `json:"tpd_leaf_publisher,omitempty"`

	// Proxy is the visor-side live proxystatus snapshot for the skysocks
	// surface — the same per-leg mux telemetry, running flag, and (when the
	// client pushes it) range-split summary the status.skysocks page renders.
	// It is OPT-IN: built only for `--select proxy`, never in the full/default
	// snapshot, so the default payload is unchanged.
	Proxy *proxystatus.Snapshot `json:"proxy,omitempty"`

	// Diag is the in-process plumbing view (router intake, transport read
	// queue and handlers, VStream muxes, dmsg ping/relay state, Go runtime) —
	// see DiagSnapshot. Cheap; part of the default snapshot.
	Diag *DiagSnapshot `json:"diag,omitempty"`

	// Roles is what this visor is FOR the network: the in-process dmsg
	// server (if any, and on which key/address), both directions of the dmsg
	// relay, and whether it refuses transit. See RolesSnapshot. Cheap; part
	// of the default snapshot.
	Roles *RolesSnapshot `json:"roles,omitempty"`

	// Notes collects per-section errors ("routing: <err>") so the snapshot is
	// self-describing about what it could and could not read.
	Notes []string `json:"notes,omitempty"`
}

StateSnapshot is a curated, secrets-free view of the visor's live runtime state, aggregated from the same RPC-safe DTOs the individual CLI subcommands return. It exists so an operator (or an agent) can introspect the whole runtime in ONE call — `skywire cli visor state` — and project exactly the field they want with the shared --jq / --shape flags, instead of stitching together a dozen subcommands.

SAFETY: every embedded field is an existing API response type, i.e. already designed to cross the RPC boundary — so this carries no mutexes, channels, contexts, live connections, or secret keys. The visor's secret key is NEVER included; identity is public-key only (via Summary.Overview.PubKey). Each section is populated best-effort: a section that errors (e.g. a subsystem not yet initialized, or the visor suspended) is left nil and the reason recorded in Notes, so a partially-up or suspended visor still returns a useful snapshot rather than failing the whole call.

type StateSnapshotReq

type StateSnapshotReq struct {
	Fields []string
}

StateSnapshotReq is the argument to StateSnapshotProjected: the subtree keys (see the StateSelect* constants) to build. Empty builds the full snapshot.

type StopAllPingsOut

type StopAllPingsOut struct {
	Stopped int
	Errors  []string
}

type Summary

type Summary struct {
	Overview     *Overview         `json:"overview"`
	Health       *HealthInfo       `json:"health"`
	Uptime       float64           `json:"uptime"`
	Routes       []RoutingRuleResp `json:"routes"`
	RouteGroups  []RouteGroupInfo  `json:"route_groups,omitempty"`
	IsHypervisor bool              `json:"is_hypervisor,omitempty"`
	// HypervisorAddr is the host:port the hypervisor UI is bound to
	// when IsHypervisor=true. Empty when this visor isn't hosting a
	// hypervisor (or when v.conf.Hypervisor is nil). Sourced from
	// v.conf.Hypervisor.HTTPAddr.
	HypervisorAddr       string                         `json:"hypervisor_addr,omitempty"`
	DmsgStats            *dmsgtracker.DmsgClientSummary `json:"dmsg_stats"`
	ConnectedDmsgServers []string                       `json:"connected_dmsg_servers"` // Deprecated: use DMSGServers instead
	DMSGServers          []DMSGServerInfo               `json:"dmsg_servers"`           // Connected DMSG servers with latencies
	Online               bool                           `json:"online"`
	// OfflineSince is set on cached summaries served when the live
	// RPC fails. Lets the UI render a "offline since X" indicator
	// alongside the otherwise-stale fields. nil when Online=true.
	OfflineSince *time.Time `json:"offline_since,omitempty"`
	// LastSeenAt is the timestamp of the last successful live
	// summary fetch. Set on both fresh and cached responses so the
	// UI can show "as of HH:MM:SS" even on stale rows. nil only on
	// the never-seen path.
	LastSeenAt           *time.Time                       `json:"last_seen_at,omitempty"`
	MinHops              uint16                           `json:"min_hops"`
	PersistentTransports []transport.PersistentTransports `json:"persistent_transports"`
	RewardAddress        string                           `json:"reward_address"`
	// RewardEligible reflects the reward system's verdict on this visor's most
	// recent survey PUSH: non-nil false = REJECTED (e.g. version below the reward
	// floor) — the hypervisor UI shows a red mark, distinct from the hyphen for no
	// reward address. nil = no verdict yet (never pushed / no reward address).
	RewardEligible         *bool  `json:"reward_eligible,omitempty"`
	RewardIneligibleReason string `json:"reward_ineligible_reason,omitempty"`
	BuildTag               string `json:"build_tag"`
	ConfigVersion          string `json:"config_version"`
	PublicAutoconnect      bool   `json:"public_autoconnect"`
	IsPublic               bool   `json:"is_public"`
	// Load is a lightweight resource snapshot (load average, mem %, disk %)
	// for the `hv ls --load` view. omitempty so the field is absent on
	// summaries from older visors that don't populate it.
	Load *LoadStats `json:"load,omitempty"`
}

Summary provides detailed info including overview and health of the visor.

type TPDAnnounceState

type TPDAnnounceState struct {
	To                string  `json:"to"`
	OK                int64   `json:"ok"`
	Failed            int64   `json:"failed"`
	SecsSinceOK       float64 `json:"secs_since_ok"`
	LastError         string  `json:"last_error,omitempty"`
	SecsSinceLastFail float64 `json:"secs_since_last_fail"`
}

TPDAnnounceState counts the announces of the transport-list feed to TPD. A Secs* field is -1 when the event has not happened.

type TPDLeafPublisherState

type TPDLeafPublisherState struct {
	Wired  bool   `json:"wired"`
	Reason string `json:"reason,omitempty"`
	// Announce is how the dials that let TPD fetch this feed are going.
	// A wired publisher whose announces fail is invisible to TPD, and the
	// route finder with it; before this the failures were Trace-only.
	Announce *TPDAnnounceState `json:"announce,omitempty"`
}

TPDLeafPublisherState reports whether the transport manager holds the CXO transport-list snapshot publisher (transport.Manager. SetTPDLeafPublisher). Wired=false means transport registration runs over the HTTP/dmsg-HTTP re-register path instead, and Reason carries why the publisher was never installed — the treestore construction error, "dmsg client absent", "stats disabled by config", or a generic "not completed init" note during the startup window.

type TPSAddTransportIn

type TPSAddTransportIn struct {
	TargetPK cipher.PubKey
	RemotePK cipher.PubKey
	TpType   string
}

type TPSExternalAddTransportIn

type TPSExternalAddTransportIn struct {
	TPSPK    cipher.PubKey
	TargetPK cipher.PubKey
	RemotePK cipher.PubKey
	TpType   string
}

type TPSExternalGetTransportsIn

type TPSExternalGetTransportsIn struct {
	TPSPK    cipher.PubKey
	TargetPK cipher.PubKey
}

type TPSRemoveTransportIn

type TPSRemoveTransportIn struct {
	TargetPK cipher.PubKey
	TpID     uuid.UUID
}

type TPSStatus

type TPSStatus struct {
	Enabled bool          `json:"enabled"`
	PubKey  cipher.PubKey `json:"pub_key,omitempty"`
}

TPSStatus contains the status of the embedded Transport Setup Node.

type TPSTransportResponse

type TPSTransportResponse struct {
	ID     uuid.UUID     `json:"id"`
	Local  cipher.PubKey `json:"local"`
	Remote cipher.PubKey `json:"remote"`
	Type   string        `json:"type"`
}

TPSTransportResponse contains information about a transport managed by TPS.

type TestResult

type TestResult struct {
	PK     string
	Max    string
	Min    string
	Mean   string
	Status string
}

TestResult type of test result

type TransportLogEntry

type TransportLogEntry struct {
	TpID      uuid.UUID `json:"tp_id"`
	RecvBytes uint64    `json:"recv_bytes"`
	SentBytes uint64    `json:"sent_bytes"`
	Timestamp int64     `json:"timestamp"`
}

type TransportRPCCallRequest

type TransportRPCCallRequest struct {
	RemotePK cipher.PubKey   `json:"remote_pk"`
	Method   string          `json:"method"`
	Args     json.RawMessage `json:"args,omitempty"` // JSON-encoded RPC arguments
}

type TransportSummary

type TransportSummary struct {
	ID      uuid.UUID           `json:"id"`
	Local   cipher.PubKey       `json:"local_pk"`
	Remote  cipher.PubKey       `json:"remote_pk"`
	Type    types.Type          `json:"type"`
	Log     *transport.LogEntry `json:"log,omitempty"`
	IsSetup bool                `json:"is_setup"`
	Label   transport.Label     `json:"label"`
	// LatencyMS is the smoothed average inter-visor RTT for this
	// transport in milliseconds, measured by transport-level
	// ping/pong (or RSN-fallback for old peers). Zero means no
	// measurement yet. Populated from tp.GetLatency().
	LatencyMS float64 `json:"latency_ms,omitempty"`
	// ThroughputBps is the passively-observed peak goodput (bytes/sec)
	// this transport has carried � a measured CAPACITY lower-bound,
	// distinct from RTT (a low-latency link can still be low-throughput,
	// e.g. webrtc). Zero until real traffic has flowed. Populated from
	// tp.GetThroughputBps().
	ThroughputBps float64 `json:"throughput_bps,omitempty"`
	// Initiator is true when this visor dialed out to establish the
	// transport (outgoing); false when it accepted an inbound dial
	// (incoming). The transport is bidirectional � this records only
	// who originated it, for in/out reporting.
	Initiator bool `json:"initiator"`
	// RemoteIP is the host portion of the underlying transport's raw
	// remote address (e.g. "1.2.3.4"). Empty for dmsg � which relays
	// through a server rather than dialing the peer directly, so an
	// empty RemoteIP is itself the "relayed, not direct" signal.
	// Populated from tp.RemoteIP().
	RemoteIP string `json:"remote_ip,omitempty"`
	// RemoteCountry is the ISO country code the embedded geoip db maps
	// RemoteIP to (the same db the routing-policy provider uses). Empty
	// when there is no direct IP (dmsg) or no geoip hit.
	RemoteCountry string `json:"remote_country,omitempty"`
	// Endpoint carries the per-transport-type low-level connection
	// metadata � the direct IP:port for stcp/stcpr/sudph/squicr, the
	// relaying dmsg server PK for dmsg, the QUIC TLS fingerprint/ALPN
	// for squicr, the selected ICE candidate for webrtc. Secrets-free
	// (dmsg = server PK only, never a secret key). nil when the
	// transport isn't serving or its type exposes no details.
	// Populated from tp.ConnDetails().
	Endpoint *network.ConnDetails `json:"endpoint,omitempty"`
}

func (TransportSummary) MarshalJSON

func (ts TransportSummary) MarshalJSON() ([]byte, error)

MarshalJSON emits id / local_pk / remote_pk only when they are set.

encoding/json's `omitempty` cannot express this: uuid.UUID and cipher.PubKey are fixed-size arrays, and omitempty never treats an array as empty � so an otherwise-blank entry still costs ~170 bytes of "000�0" on the wire. That is what makes the node-list projection worth doing at all (see compactTransportSummaries, which emits type and direction only). A real transport has all three set and serializes exactly as it did before.

type Transports

type Transports interface {
	TransportTypes() ([]string, error)
	Transports(types []string, pks []cipher.PubKey, logs bool) ([]*TransportSummary, error)
	Transport(tid uuid.UUID) (*TransportSummary, error)
	AddTransport(remote cipher.PubKey, tpType string, timeout time.Duration, label string, noRegister bool, skipLatencyProbe bool) (*TransportSummary, error)
	SetSTCPAddr(pk cipher.PubKey, addr string) error
	RemoveTransport(tid uuid.UUID) error
	RemoveAllTransports() error
	SetPublicAutoconnect(pAc bool) error
	SetIsPublic(isPublic bool) error
	GetIsPublic() bool
	LocalTransportStats() (*LocalTransportStatsResponse, error)
	LocalUptimeStats(args LocalUptimeArgs) (*LocalUptimeResponse, error)
	StartPublicAutoconnect() error
	StopPublicAutoconnect() error
	PublicAutoconnectStatus() (bool, error)
	GetPersistentTransports() ([]transport.PersistentTransports, error)
	SetPersistentTransports([]transport.PersistentTransports) error
	GetTransportLogs(days int) ([]TransportLogEntry, error)
	DiscoverTransportsByPK(pk cipher.PubKey) ([]*transport.Entry, error)
	DiscoverTransportByID(id uuid.UUID) (*transport.Entry, error)
	// Embedded Transport Setup Node (TPS) controls
	TPSStatus() (*TPSStatus, error)
	TPSAddTransport(targetPK, remotePK cipher.PubKey, tpType string) (*TPSTransportResponse, error)
	TPSRemoveTransport(targetPK cipher.PubKey, tpID uuid.UUID) error
	TPSGetTransports(targetPK cipher.PubKey) ([]TPSTransportResponse, error)
	// External TPS operations (dial external TPS over dmsg)
	GetTransportSetupNodes() ([]cipher.PubKey, error)
	GetTransportSetupNodesSorted() ([]cipher.PubKey, error)
	GetTPSHealth() ([]NodeHealth, error)
	TPSExternalHealthCheck(tpsPK cipher.PubKey) error
	TPSExternalAddTransport(tpsPK, targetPK, remotePK cipher.PubKey, tpType string) (*TPSTransportResponse, error)
	TPSExternalGetTransports(tpsPK, targetPK cipher.PubKey) ([]TPSTransportResponse, error)
}

Transports is the visor's transports, transport discovery and transport setup.

type TransportsIn

type TransportsIn struct {
	FilterTypes   []string
	FilterPubKeys []cipher.PubKey
	ShowLogs      bool
}

type UDPForwardIn

type UDPForwardIn struct {
	RemotePK   cipher.PubKey
	RemotePort int
	LocalPort  int
}

UDPForwardIn is the argument for DialUDPForward (#2607): dial a remote forwarded_ports.udp service and bridge it to a local UDP socket.

type UIServerStatus

type UIServerStatus struct {
	Running   bool   `json:"running"`
	LocalAddr string `json:"local_addr,omitempty"`
	DmsgPort  uint16 `json:"dmsg_port,omitempty"`
}

UIServerStatus contains the status of the UI server.

type UptimeHistoryArgs

type UptimeHistoryArgs struct {
	Since           time.Time
	Limit           int
	IncludeTimeline bool
	// TimelineDate, when set with IncludeTimeline, returns the bitmap
	// for that UTC date instead of today.
	TimelineDate time.Time
}

UptimeHistoryArgs is the request shape for API.UptimeHistory. All fields are optional: empty Since reads everything in retention, zero Limit returns all rows, IncludeTimeline=false skips the timeline byte slice.

type UptimeHistoryResponse

type UptimeHistoryResponse struct {
	Current      serviceuptime.SessionRecord   `json:"current"`
	Sessions     []serviceuptime.SessionRecord `json:"sessions"`
	Timeline     []byte                        `json:"timeline,omitempty"`
	TimelineDate string                        `json:"timeline_date,omitempty"`
}

UptimeHistoryResponse carries the visor's own session history. Current is the in-flight session row; Sessions is everything the recorder has retained, oldest first. Timeline (when requested) is the 36-byte 288-slot bitmap for TimelineDate, suitable for rendering with serviceuptime.FormatBitmap.

type VisorCatRequest

type VisorCatRequest struct {
	// Mode is VisorCatModeDial or VisorCatModeListen.
	Mode string `json:"mode"`
	// RemotePK is the peer's visor PK. Required in dial mode;
	// ignored in listen mode (the listener accepts whatever PK the
	// dmsgscp whitelist authorizes).
	RemotePK cipher.PubKey `json:"remote_pk,omitempty"`
	// Port is the remote port to dial in dial mode, or the local
	// port to listen on in listen mode.
	Port uint16 `json:"port"`
	// Transport is VisorCatTransportDmsg or VisorCatTransportSkynet.
	// Empty defaults to dmsg.
	Transport string `json:"transport,omitempty"`
	// Timeout bounds the dial (dial mode) or the accept-wait (listen
	// mode) plus the splice. Zero defaults: 60s for dial, 5m for
	// listen.
	Timeout time.Duration `json:"timeout,omitempty"`
	// Routes requests N parallel mux routes for the skynet transport
	// in dial mode. The router opens N route-groups in parallel and
	// stripes writes across them with sequence numbers + resequencing
	// on reads — bytes remain ordered at the app layer. Ignored for
	// dmsg transport and for listen mode (listener accepts whatever
	// the peer dials). 0 or 1 = single route (default).
	Routes int `json:"routes,omitempty"`
}

VisorCatRequest describes a stream-splice the visor should perform on the caller's behalf. The visor process opens the remote stream (dial or listen) over the chosen transport and exposes one end as a 127.0.0.1 loopback TCP listener — bytes never traverse the local RPC channel beyond the initial request + response.

type VisorCatResponse

type VisorCatResponse struct {
	// LocalAddr is "127.0.0.1:PORT" the CLI dials to bridge stdio.
	LocalAddr string `json:"local_addr"`
}

VisorCatResponse carries the 127.0.0.1 loopback address the CLI should dial. The visor's accept-and-splice goroutine waits on this listener; the CLI's dial completes the splice loop.

type VisorSCPRequest

type VisorSCPRequest struct {
	// RemotePK is the peer's visor PK. The peer must have a dmsgscp
	// host bound and the caller's PK on the host's whitelist.
	RemotePK cipher.PubKey `json:"remote_pk"`
	// Port is the peer's dmsgscp port (same for both transports —
	// dmsgscp.DefaultPort is 23).
	Port uint16 `json:"port"`
	// Direction is VisorSCPUpload or VisorSCPDownload.
	Direction string `json:"direction"`
	// LocalPath is the path on the visor host's filesystem. For an
	// upload it's the source; for a download it's the destination.
	LocalPath string `json:"local_path"`
	// RemotePath is the path relative to the peer's rootDir. For an
	// upload it's the destination; for a download it's the source.
	// Absolute paths and `..` components are rejected by the peer.
	RemotePath string `json:"remote_path"`
	// Transport is VisorSCPTransportDmsg or VisorSCPTransportSkynet.
	// Empty defaults to dmsg.
	Transport string `json:"transport,omitempty"`
	// Timeout bounds the whole transfer (dial + scp protocol +
	// payload). Zero falls back to a 5-minute default.
	Timeout time.Duration `json:"timeout,omitempty"`
}

VisorSCPRequest describes a peer-to-peer file transfer the visor should perform on the caller's behalf. The visor process opens the stream over the chosen transport (it has both dmsgC + appnet resolvers wired in, where a standalone CLI does not) and drives the dmsgscp wire protocol entirely inside the visor.

Local files are read/written on the visor host's filesystem. Operators running the CLI as the same user as the visor process see the obvious "this is my $HOME" behavior.

type VoiceAudioSnapshot

type VoiceAudioSnapshot struct {
	Sent []int16
	Recv []int16
}

VoiceAudioSnapshot is the recent sent/received PCM of an active call.

type VoiceDialingInfo

type VoiceDialingInfo struct {
	CallID string `json:"call_id"`
	Peer   string `json:"peer"`
	// State is how far the call has got: "connecting", "calling" (the peer
	// has the invite but predates ringing reports) or "ringing" — or, for a
	// few seconds after it ended unanswered, why: "offline", "declined",
	// "busy", "no_answer" or "failed". Empty from a visor that predates it.
	State string `json:"state,omitempty"`
	// Reason is the detail behind an outcome.
	Reason string `json:"reason,omitempty"`
	// Ringback is true once the peer's own ringback tone has arrived and can
	// be played (VoiceDialRingback); false means play an ordinary ring.
	Ringback bool `json:"ringback,omitempty"`
}

VoiceDialingInfo is one call this visor is placing, before it is answered.

type VoiceMuteReq

type VoiceMuteReq struct {
	CallID  string
	Mic     bool
	Speaker bool
}

VoiceMuteReq toggles the mic/speaker mute of a call.

type VoiceRingback added in v1.3.98

type VoiceRingback struct {
	Data []byte `json:"data,omitempty"`
	Mime string `json:"mime,omitempty"`
}

VoiceRingback is a ringback tone: what a caller hears while a visor rings. Empty Data means none.

Jump to

Keyboard shortcuts

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