Documentation
¶
Overview ¶
Package clirpc cmd/skywire-cli/commands/rpc/root.go c4-vis-cli
Package clirpc cmd/skywire-cli/commands/rpc/routingsession.go c4-vis-cli
Package clirpc cmd/skywire-cli/commands/rpc/verbose.go c4-vis-cli
Verbose log-streaming helper for cli commands that block on a visor-side RPC and want to surface what the visor is doing in real time.
Two APIs:
OpenVerbose returns a *VerboseStream the caller drives by hand. Use this when the calling command needs to interleave the stream with its own RPCs (proxy start / vpn start: subscribe, start app, poll for Running, wait on ctx.Done, stop app).
WithVerbose wraps a single bounded operation. Use this for the RPC-and-done shape (dmsg curl, skynet curl, tp add, dmsg probe). Callers in this form don't need to know the stream exists when --verbose is off — the helper short-circuits.
Both wait for a subscribe-handshake sentinel before the wrapped action runs so the first ~50ms of visor activity isn't lost to the stream-handshake race.
Index ¶
- Constants
- Variables
- func ApplyRoutingSession(rc visorapi.API, o RoutingSessionOpts) error
- func BridgeConn(scheme byte, remotePK cipher.PubKey, port uint16) (net.Conn, error)
- func Client(cmdFlags *pflag.FlagSet) (visorapi.API, error)
- func ClientQuiet(cmdFlags *pflag.FlagSet) (visorapi.API, error)
- func DmsgClient(cmdFlags *pflag.FlagSet) (visorapi.API, error)
- func DmsgURLForHTTP(httpURL string) string
- func FetchCachedServiceURL(cmdFlags *pflag.FlagSet, cachefile, thisurl string, cacheFilesAge int) string
- func FetchIntegratedUptimes(cmdFlags *pflag.FlagSet, tpdBase, cachefile string, cacheFilesAge int) string
- func FetchIntegratedUptimesDays(cmdFlags *pflag.FlagSet, tpdBase, cachefile string, cacheFilesAge, days int) string
- func FetchServiceURL(cmdFlags *pflag.FlagSet, url string) ([]byte, error)
- func IntegratedUptimeURL(tpdBase string) string
- func IntegratedUptimeURLDays(tpdBase string, days int) string
- func RegisterFetchFlags(cmd *cobra.Command)
- func WithVerbose(ctx context.Context, rpcAddr string, filter VerboseFilter, fn func() error) error
- type FetchConfig
- type RoutingSessionOpts
- type VerboseFilter
- type VerboseStream
Constants ¶
const (
// RPCAddrEnvVar is the environment variable name for the RPC server address
RPCAddrEnvVar = "SKYWIRE_RPC"
)
Variables ¶
var ( // DefaultRPCAddr is the default RPC address (from env var or default value) DefaultRPCAddr = getDefaultRPCAddr() // Addr is the address (ip:port) of the rpc server Addr string // Timeout is the timeout for RPC calls in seconds (0 = unlimited) Timeout int = 30 // VisorPK is the public key of the visor to connect to over dmsg VisorPK string // CliSK is the secret key for authenticating CLI over dmsg CliSK string // Via, when non-empty, requests proxy to a remote visor through // the local visor named by --rpc. Supported schemes: // - "dmsg://<pk>" — local visor opens a dmsg stream to // <pk>:DmsgVisorRPCPort // and bridges bytes back to this CLI (multiplexed onto the // existing --rpc port via cmux + magic-byte prefix). // - "skynet://<pk>" — local visor proxies via TransportRPCCall // (route ID 0 / VisorRPCPacket). Via string )
var ( // NoCXO disables the CXO subscriber step in FetchServiceURL. NoCXO bool // NoRPC disables the RPC step in FetchServiceURL. NoRPC bool // NoDmsg disables the direct DMSG HTTP step in FetchServiceURL. NoDmsg bool // FetchSK, when set, is the secret key to use for the direct DMSG HTTP // step's ephemeral client. Otherwise the precedence in resolveFetchIdentity // applies (--config -> $DMSG_SK -> $DMSGCURL_SK -> ephemeral random). FetchSK cipher.SecKey // FetchConfigPath is a path to a JSON config supplying the SK and an // optional dmsg-bootstrap section. Lets operators keep the SK out of // shell history. See FetchConfig for the file shape. FetchConfigPath string )
Functions ¶
func ApplyRoutingSession ¶
func ApplyRoutingSession(rc visorapi.API, o RoutingSessionOpts) error
ApplyRoutingSession applies the non-nil options in canonical order via the visor RPC:
- mux-mode "" / "auto" → skipped (router's own default weighting).
- min-hops 0 → error (0 disables routing).
There is deliberately no route-COUNT option here. It used to set a visor-wide mux_routes, which was wrong twice over. Mechanically, a per-invocation flag mutated persistent visor state (router runtime, the live networker's dial default, and a config flush), so `proxy start --mux 2` left every later plain `proxy start` on that visor silently multiplexed, across restarts. Worse, the visor-global default was then applied to every dial including --direct ones, which suppressed the 0-hop AppDirectMux shortcut and pushed dials that should never touch route setup through the setup node.
The deeper reason is that a route count is not a static property to configure at all. How many routes a flow should hold depends on its bandwidth demand and on which transports and intermediates exist between those two visors at that moment — and transports come and go. That is a decision for the routing policy, which already computes a live degree and holds warm standby legs (preset.AdaptRevActive()+AdaptStandbyMax(), re-capped each tick via router.SelfHealTargeter). A fixed dial-time number fought it: the self-heal storm toward a stale dial-time degree is exactly that conflict. Reshape a live session with `cli proxy mux width/standby` instead.
func BridgeConn ¶
bridgeClient dials the local visor's CLI RPC port (default localhost:3435), sends the bridge magic + header (with the scheme byte selecting dmsg or skynet), and returns an rpc.Client running over the bridged conn. The visor opens the underlying stream using its own identity — no separate CLI keypair needed. BridgeConn dials the local visor's CLI RPC port, sends the bridge magic + header, and returns the RAW bridged conn (no rpc.Client wrapper). The visor opens the underlying stream to (remotePK, port) using ITS OWN identity — for scheme=0 (dmsg) that is the visor's authorized dmsg client, so a peer's dmsgpty whitelist sees the visor's PK (e.g. the implicitly-allowed hypervisor) rather than a throwaway CLI key, and there is no dmsg-discovery PK collision.
Callers drive whatever protocol they like over the conn and own its lifecycle. `cli pty fs mount <pk>` uses this for its via-visor path (port = skyenv.DmsgPtyPort, then the sftp-subsystem handshake); bridgeClient uses it for the `--via` RPC path (port = visor RPC).
func Client ¶
Client is used by other skywire-cli commands to query a visor RPC. The --rpc flag supports several schemes:
- "host:port" (default "localhost:3435") — direct TCP to the local visor.
- "skynet://<pk>" — route the call to a REMOTE visor over a skywire transport (stcpr/sudph). The CLI dials the local visor as usual; every typed visor.API method gets transparently proxied through the local visor's TransportRPCCall. The local visor's PK must be in the remote visor's hypervisor or dmsgpty whitelist. Transport is auto-created (stcpr→sudph fallback) if one doesn't exist yet.
- "dmsg://<pk>" — direct dmsg dial to the remote visor's RPC server. Currently NOT implemented (requires a server-side listener that doesn't exist yet); returns an explicit error.
On dial failure, prints the error to stderr via internal.PrintError so one-shot callers see the cause. Long-running reconnect loops (group listen, skychat listen) should use ClientQuiet to suppress per-retry spam.
func ClientQuiet ¶
ClientQuiet is the silent variant of Client. On dial failure it returns the error WITHOUT printing to stderr. Use this from any callsite that retries on failure (a noisy loop spamming the same "RPC connection failed" line is worse than no message at all — the caller knows the dial failed because err is non-nil).
func DmsgClient ¶
DmsgClient creates an RPC client over dmsg
func DmsgURLForHTTP ¶
DmsgURLForHTTP looks up the DMSG equivalent URL for an HTTP service URL. Returns empty string if no DMSG address is known for the service.
Exported so callers outside cmd/skywire-cli/commands/rpc (notably cli log) can rewrite a service URL to its dmsg-form before fetching over a caller-owned dmsg.Client, avoiding a plain-HTTP hop through the deployment-services HTTP edge.
func FetchCachedServiceURL ¶
func FetchCachedServiceURL(cmdFlags *pflag.FlagSet, cachefile, thisurl string, cacheFilesAge int) string
FetchCachedServiceURL is a drop-in replacement for cliutil.GetData that routes the fetch through FetchServiceURL (RPC → DMSG → HTTP) instead of going straight to HTTP.
Caching semantics:
- If cachefile == "", caching is disabled and we fetch fresh every call. Otherwise the cachefile argument is treated as a non-empty marker for "caching enabled"; the actual storage is a single bbolt DB at pkg/clicache's DefaultPath (NOT the per-URL JSON file the legacy code used). The bbolt approach replaces the older /tmp/<host>/<endpoint>.json files which couldn't be shared across users due to umask downgrade.
- If a cached entry exists and is younger than cacheFilesAge minutes, its body is returned as-is.
- Otherwise we fetch via FetchServiceURL and write the response through to the bbolt cache for the next call.
- On fetch failure, a stale cached entry is returned as a last resort rather than propagating an empty string — UNLESS the caller passed cacheFilesAge == 0, which signals "I want fresh data, no fallback". A loud failure is better than a silent stale response for callers that explicitly opted out of caching (e.g. nightly reward calcs that script against today's date and have no way to detect that the body they got was yesterday's).
- When a stale entry IS returned, a warning is logged so callers who didn't opt out can still notice the data isn't fresh.
Returns the response body as a string, or "" if every path failed and there was no cache to fall back on. Errors are logged at debug level.
func FetchIntegratedUptimes ¶
func FetchIntegratedUptimes(cmdFlags *pflag.FlagSet, tpdBase, cachefile string, cacheFilesAge int) string
FetchIntegratedUptimes fetches the []VisorSummary v3 payload from the TPD-integrated uptime tracker via the shared cached fetch chain (CXO→DMSG→HTTP + disk cache). tpdBase is the transport-discovery base URL (empty = prod). The returned JSON has the same {pk, on, ...} shape v2 had, so existing jq filters keep working. Returns "" only when every fetch path failed AND there was no cache to fall back on; callers must treat "" as "no online data" and must not build JSON from it (an empty string is not valid JSON).
func FetchIntegratedUptimesDays ¶
func FetchIntegratedUptimesDays(cmdFlags *pflag.FlagSet, tpdBase, cachefile string, cacheFilesAge, days int) string
FetchIntegratedUptimesDays is FetchIntegratedUptimes with an explicit uptime window (days ∈ {1,7,30}). Online-status-only callers (visor list / online filters) pass days=1: the .on flag is present in every window, so pulling the 30-day daily bitmap for thousands of visors is pure over-fetch that bloats the payload past the RPC DmsgHTTP timeout. Matches the days=1 usage in pv / sd / proxy / vpn.
func FetchServiceURL ¶
FetchServiceURL fetches a URL from a deployment service over a CXO/DMSG-only chain — there is NO plain-HTTP fallback:
- CXO — read the visor's local subscriber-cache snapshot (no round-trip)
- RPC — ask the running visor to proxy the request over DMSG (DmsgHTTP RPC)
- DMSG direct — ephemeral DMSG client (only when --no-rpc)
The plain-HTTP hop was removed deliberately: deployment services are all dmsg-mapped (see DmsgURLForHTTP), the clearnet fallback was firing erroneously, and there's no value in maintaining it — for ad-hoc browser access to deployment data, point the browser at the visor's dmsgweb resolving proxy. Steps can be disabled via --no-cxo, --no-rpc, and --no-dmsg.
func IntegratedUptimeURL ¶
IntegratedUptimeURL builds the URL for the TPD-integrated uptime tracker's visor-uptime endpoint: the transport-discovery base with ?v=v3. v3 is the version cxoFeedForURL mirrors over CXO (feed "tpd-uptime", path uptimes/days/30), so a fetch through FetchCachedServiceURL / FetchIntegratedUptimes flows over the CXO→DMSG→HTTP chain and NEVER dials the deprecated standalone uptime tracker. An empty tpdBase falls back to deployment.Prod.TransportDiscovery.
v3 is a strict superset of v2: every entry still carries {pk, on, version, daily}, with the per-day timeline bitmap added. Consumers that only read the online flag or the daily percentages therefore work against the v3 payload unchanged.
func IntegratedUptimeURLDays ¶
IntegratedUptimeURLDays is IntegratedUptimeURL with an explicit uptime window: days ∈ {1,7,30}, the buckets the TPD publishes (uptimes/days/<n>). Callers that only read the online flag (pv / sd / proxy filtering) pass days=1 — the .on flag is present in every window, so pulling the 30-day daily bitmap for every visor is pure over-fetch. Daily-percentage callers (the `ut` command) pass a wider window. An unknown value falls back to 30 in cxoFeedForURL.
func RegisterFetchFlags ¶
RegisterFetchFlags adds the fetch-path flags to a command. Call this in init() for any command that uses FetchServiceURL. There is no plain-HTTP step — deployment services are reached over CXO/DMSG only; for ad-hoc browser access, point the browser at the visor's dmsgweb resolving proxy instead of relying on a clearnet hop.
Flags:
--no-cxo skip the CXO subscriber-cache step.
--no-rpc skip the visor RPC (DmsgHTTP) step; forces fall-through to the
direct DMSG step using a CLI-owned dmsg client.
--no-dmsg skip the direct DMSG HTTP step.
--sk secret key (hex) for the CLI-owned dmsg client when the direct
step runs (--no-rpc or RPC unavailable). Visible in shell
history — prefer --config for production use.
--config path to a JSON file supplying {"sk":..., "dmsg":{...}}. SK
precedence: --sk > --config > $DMSG_SK > $DMSGCURL_SK > random
ephemeral. Dmsg.Servers from the config seeds the direct
client; absent, the embedded deployment server set is used.
func WithVerbose ¶
WithVerbose is the function-wrapper convenience. If filter is the zero value (empty AppName + empty Modules), it is a no-op and just runs fn. Otherwise it opens the stream, waits up to 2s for the subscribe handshake, runs fn, and waits for the stream goroutine to exit (ctx-canceled by the caller or naturally drained).
Returns whatever fn returned.
Types ¶
type FetchConfig ¶
type FetchConfig struct {
SK cipher.SecKey `json:"sk,omitempty"`
PK cipher.PubKey `json:"pk,omitempty"`
Dmsg dmsgc.DmsgConfig `json:"dmsg,omitempty"`
}
FetchConfig is the minimal CLI-side identity/dmsg-bootstrap config consumed by --config. The schema is compatible with a visor's skywire.json: top-level "sk"/"pk" + a "dmsg" block with "discovery_dmsg" and "servers", so the same file can be reused for both purposes. Extra fields in the file are ignored.
type RoutingSessionOpts ¶
type RoutingSessionOpts struct {
ExistingTP *bool // SetExistingTPOnly
LocalRoute *bool // SetForceLocalRoutes
MuxMode *string // SetMuxMode — "" / "auto" skipped (router default)
MinHops *uint16 // SetMinHops — 0 is rejected (disables routing)
}
RoutingSessionOpts carries the routing-session options shared by the app-start surfaces (`cli proxy start`, `cli vpn start`, `cli visor app start`). Every field is a pointer so the caller applies ONLY what it wants set — a nil field leaves that visor setting untouched. This lets each command keep its own flag/Changed semantics while the actual RPC sequence (and its quirks) lives in exactly one place, so the three surfaces can't drift.
type VerboseFilter ¶
type VerboseFilter struct {
// AppName matches by _module exact equality OR app/app_name
// field. Empty when scoping by Modules only (e.g. dmsg curl).
AppName string
// Modules is a prefix list matched against the _module field.
// OR-merged with AppName.
Modules []string
// Level is the minimum severity ("trace"|"debug"|"info"|"warn"|"error").
// Empty defaults to "debug".
Level string
}
VerboseFilter is the per-command filter spec passed to the gRPC StreamAppLogs request.
type VerboseStream ¶
type VerboseStream struct {
// contains filtered or unexported fields
}
VerboseStream is a live subscription to the visor's master logger. Entries are formatted and written to stderr by a goroutine until ctx is canceled or Close is called.
func OpenVerbose ¶
func OpenVerbose(ctx context.Context, rpcAddr string, filter VerboseFilter) (*VerboseStream, error)
OpenVerbose dials the visor's gRPC endpoint and starts streaming matching entries to stderr. Returns immediately; caller calls WaitSubscribed before the action they want to observe, and Close when done.
Either filter.AppName or filter.Modules must be non-empty.
rpcAddr is typically clirpc.Addr; the gRPC server is multiplexed onto the same port as the standard RPC server via cmux.
func OpenVerboseTo ¶
func OpenVerboseTo(ctx context.Context, rpcAddr string, filter VerboseFilter, w io.Writer) (*VerboseStream, error)
OpenVerboseTo is OpenVerbose with an explicit destination writer for the formatted entries. `proxy start --verbose` streams to stderr (via OpenVerbose); `cli proxy log` streams to stdout so the log is the command's primary output. Behavior is otherwise identical.
func (*VerboseStream) Close ¶
func (v *VerboseStream) Close()
Close waits for the stream goroutine to exit, tearing down the underlying gRPC client. Safe to call once. Caller is responsible for canceling ctx (or letting it cancel naturally) so the stream goroutine returns.
func (*VerboseStream) WaitSubscribed ¶
WaitSubscribed blocks until the server confirms the subscription is live (the Subscribed sentinel was received) or timeout fires. Returns context.DeadlineExceeded on timeout — callers usually proceed anyway, since the stream may still attach mid-action.