Back to list

Development Update — August 3

A heavy day on the reward system: the survey-collection pipeline that had lived as host-only bash (fetch_surveys.sh + getlogs.sh) got ported into Go, made a drop-in for the live host, and paired with a new push model where a visor POSTs its own survey to the reward system over dmsg — closing the silent-drop gap where a demonstrably-up visor could earn nothing because a pull snapshot happened to read it offline. Around it: the systray decoupled from the visor process so a background service can be driven by an unprivileged tray, the pty-exec gate was narrowed to self-only so hypervisor control of remote visors works again by default, skynet source-routes got warm-route reuse, skychat gained a dmsg-first auto mode, and the network visualizer started counting every transport type it actually has.

Skywire: The Reward System, Ported to Go

Six PRs turn the host-only bash survey collector into a correct pure-Go replacement. 3675 fix(rewards): record “survey not found” as ineligible is the observability half — the calc iterated the PKs that already met daily uptime, and if a survey was missing it did log.Debug; continue before adding the PK to the ineligible list, so an up visor whose survey wasn’t collected showed up as neither rewarded nor ineligible-with-reason, an invisible dash a real operator hit. It now emits an explicit Reason: "survey not found" row (payout-neutral; the ineligible list is not the payout list). 3676 hardens the Go collector to match fetch_surveys.sh: collect every seen-today visor rather than only those with the uptime tracker’s instantaneous Online flag set (the flag-offline-but-reachable case is exactly what cost the operator their rewards), use the /health fetch as both the reachability gate and the authoritative version source, and add a two-pass retry so a first-contact cold-dmsg-session timeout no longer drops a reachable visor. 3685 adds --proxy host:port so the collector can fetch through a running skywire dmsg web SOCKS5 resolving proxy that already holds warm dmsg sessions (visors reached as http://<pk>.dmsg/…, resolved and dialed by the proxy) — exactly what the live host’s curl --socks5-hostname does. 3686 adds a Go-native syncToBackup that stages log_collecting → log_backups (an additive recursive copy mirroring rsync -r without --delete), the missing step that had kept the Go collector from being a drop-in for _getlogs && _cleanup. 3687 threads --proxy onto rewards run, and 3690/3691 add --minv auto, a Go port of getlogs.sh’s _compute_minversion that resolves the dynamic 14-day reward floor (latest release published ≥14 days ago) — needed because the calc doesn’t gate on version, so survey collect/prune is the sole version-eligibility gate and a static floor would let below-floor visors be paid. Three follow-ups fixed the uptime-tracker fetch on the live host: 3692 first special-cased it via RPC→DMSG, then 3693 found the real cause was the collector defaulting --ut to the decommissioned standalone uptime tracker (dmsg error 202 - cannot connect to delegated server) and switched it to the TPD-integrated /uptimes, after which 3694 dropped the special case entirely once it was verified live that a survey-whitelisted key reaches every deployment service over the proxy. 3688 documents the whole system end-to-end (rewards/operations.md) — components and data flow, both collection mechanisms, the shared calc math, the reward-host systemd units, and the live-swap recipe retiring the bash for skywire cli log --proxy 127.0.0.1:4443 --cleanup.

Skywire: Survey Push over dmsg

The durable fix for the silent-drop gap is to stop depending on a pull snapshot at all. 3677 feat(rewards): accept visor survey PUSH over dmsg (POST /node-info) is the receiving half, served on the reward server’s existing inbound dmsg listener — a visor POSTs its node-info survey, stored under the sender’s dmsg-authenticated source PK (via the new rewards.RemotePK), so a visor can only ever write its own survey, and the survey’s self-reported pk must match the authenticated sender or it’s rejected. A GET /node-info/stored-checksum returns the sha256 the reward system holds for the requester’s own survey so the visor can skip an unchanged push (a conditional PUT — “only transfer on change” without CXO’s per-feed machinery), and an ineligible skywire_version is refused with an explicit {"eligible":false,"reason":…} so the visor can surface it. 3679 is the visor-side push client: reward_push.go runs a per-visor loop tied to the module ctx (so Suspend cancels it and Resume starts a fresh one), each cycle a conditional PUT — GET the stored checksum, POST the full survey only when it differs — a no-op until a reward address is set and reward_system_dmsg is configured, with the accept/reject verdict recorded in Summary as RewardEligible *bool + RewardIneligibleReason. 3680 feat(hvui): three-state reward column surfaces that verdict in the node list: grey em-dash for no reward address, red ❌ + tooltip for a survey the reward system rejected (e.g. version below floor), green 🟢 for eligible or no-verdict-yet — so an operator can see why an up, address-set visor still isn’t earning, and red now means “something’s wrong” instead of “no address.” Separately, 3678 fixes the reward server’s /stats page erroring with unsupported protocol scheme "dmsg"getTPDNetworkSummary fetched a dmsg://<pk>:80/metrics URL with a bare http.Get, whose default client has no dmsghttp RoundTripper; it now routes through the dmsg client the server already builds at serve time.

Skywire: Decoupled Systray and a Narrower pty Gate

3669 feat(systray): revised menu + decoupled tray (--systray-only) builds on the Suspend/Resume RPC. The tray’s controls already talked to the visor purely over its local RPC, so the only coupling was runAppSystray starting a visor in-process; a new runTrayOnly() + --systray-only flag runs only the tray, connecting to whichever visor is already serving on cli_addr and never starting its own — which lets the visor run as an always-on background service while the tray is an unprivileged desktop app that can suspend/resume it with no privilege escalation, the model Suspend/Resume was built for. The revised menu genericizes the VPN submenu into a reusable appTray (with a matching skysocks-proxy submenu), adds the Suspend/Resume toggle and a public-autoconnect checkbox, and reaches root parity. 3689 fix(pty): only gate RPC-exec on the SELF target narrows #3658’s opt-in gate: #3658 blanket-disabled API.DmsgPtyExec, which also broke a hypervisor legitimately driving the pty of the remote visors it manages. But DmsgPtyExec always dials args.RemotePK’s dmsgpty — the genuine local-privesc vector is only when RemotePK == this visor's own PK (loopback to the local dmsgpty host, a shell as the host identity), while exec to a remote PK is gated by the remote’s own whitelist, the correct trust boundary. So the AllowRPCExec gate now applies only on self/loopback; remote targets are always allowed, restoring cli pty exec <remote-pk> by default.

Skywire: Warm Skynet Routes and Skychat Auto-Mode

3672 feat(skynet): pool + yamux-reuse explicit source-route dials extends the resolving proxy’s route-reuse pool to cover explicit <hop>.<dest>.skynet routes. The proxy already held one warm, keepalive’d route group per destination for plain <pk>.skynet fetches, but explicit source routes skipped that path — dialSourceRoute dialed a fresh single-path route per connection, so a manually-pinned multihop route died and re-set-up on every page load. skyroute.Pool is generalized to key on an opaque string with a per-call dial closure (route-agnostic), and the proxy keys the plain path on the destination hex and the source-route path on destination + hop-set, so the two hold independent warm groups and distinct hop-sets to the same dest each stay warm; dialSourceRoute tries the pooled mux route first and falls back to a fresh 1:1 dial against older far ends that don’t serve the mux port. 3702 feat(skychat): auto network mode adds an auto selector option (now the default) that lets the shared DM controller pick the transport: a warm conn is reused (so a reply follows the path the peer established, no wire change), and with no warm conn the first message goes over dmsg immediately (always up, no route setup = instant first contact) while a skynet route warms in the background so the next message upgrades to the faster routed transport (~7ms routed vs ~178ms direct dmsg); warmNetwork closes the superseded dmsg conn on success so its read goroutine exits and no duplicate lingers. 3695 makes skychat default to skynet in the wasm visor and keeps embed=1 chrome-less only when actually iframed, and skychat also gained published profiles, an address book, sidebar tabs, saved messages, and origin-private forwarding.

Skywire: The Network Visualizer, Counting Everything

A run of gap-fixes brought the transport-graph visualizer up to reality. 3696 feat(netview): count all transport types is the backend half — netview.Compute (shared by the native HV and the wasm visor) bucketed transports into only STCPR/SUDPH/DMSG/STCP, so QUIC (squicr), WEBRTC, WS (swsr) and WT (swtr) edges counted in Total but had no per-type breakdown; it now drives the switch off tptypes.NormalizeType so legacy wire names fold into the canonical bucket and the set stays aligned with tptypes.Known(), with additive JSON fields. 3700 feat(tpviz): per-type stats for all transports fixes the same undercount in the reward-site/HV sidebar (counting only 3 of 8 types) and folds the decommissioned standalone “Uptime Tracker” section into “Transport Discovery.” 3701 feat(hv-ui): color nodes by country + full transport tooltip fills each node by a stable hashed country hue with UT status kept as the border, and lists every transport type in the per-node tooltip. 3698 fixes the WebGL renderer dropping visors whose only transport was filtered out (Flat view keeps all nodes) and dots bleeding past their group bubble. 3706 docs(rfc): GUI embedding standardization is the architecture RFC behind the next phase — an inventory of every GUI in the default build and a decision rule (iframe only for untrusted/foreign content or a self-contained runtime; otherwise mount the bundle into a <div> in the host’s own JS/DOM context) — with a migration order that pilots on the network visualizer, and the day closed by mounting the shared pkg/tpviz visualizer directly in the HV tab and retiring the Angular vis-network reimplementation. On the phone, the first app screen landed — a SkySOCKS screen (server list from service discovery, connect, expose-port, scoped logs) that re-pins --addr 127.0.0.1 and --reconnect on every launch, verified end-to-end on a real device — alongside an embedded, phone-fitted Chat tab.