Back to list
Jul 28 2026

Development Update — July 28

The urgent item today is a reward regression the previous release introduced: after v1.3.89 restored the reward heartbeat, roughly half the fleet was recording only ~30% uptime while actually up — below the 75% reward threshold, so earning nothing. The fix runs the failure down to heartbeat under-delivery rather than any server-side scoring bug, and adds bounded backfill so flaky delivery no longer maps one-to-one to sparse uptime. Around it, the TinyGo wasm-visor got unwedged (a reflection panic that only fired inside the HV worker), the Bitcoin wallet’s electrum backend moved to where every other coin’s node lives, a flaky routing test got a real correctness fix behind it, and v1.3.91 was cut.

Skywire: The Reward-Uptime Regression, Fixed

3619 fix(reward-uptime): heartbeat delivery + bounded backfill (v1.3.89 ~30% uptime regression) is the headline. About half the fleet — v1.3.89-dominated — was recording ~30% uptime while continuously up, dropping it below the 75% reward bar; the 25–35% cluster was 439 of 783 visors, while the reward-eligible ≥75% group was dominated by older versions. The cause was heartbeat under-delivery, not the server: the visor’s 5-minute uptime ticker ran the send inline, so a slow send — TPD auth contending with the ~90s transport re-registration through the shared httpauth nonce, aggravated by TPD-host load — made time.Ticker drop ticks, and the server’s correct-but-literal RecordHeartbeat then set exactly one slot per landed heartbeat, mapping sparse delivery straight to sparse uptime. Transport-hub visors stayed at 100% only because their frequent re-registration also records slots; heartbeat-dependent visors collapsed. The fix is two-sided: the visor now runs each heartbeat round in its own goroutine bounded by a 2-minute timeout (a 1-slot semaphore prevents pile-up) so a slow send can never stall the ticker, and fires an immediate heartbeat on start instead of forfeiting the first tick; the server’s RecordHeartbeat now backfills timeline slots from the previous heartbeat to now, bounded to 30 minutes and same-day only, so flaky delivery still credits a continuously-up visor at ~100% while a genuine longer absence is not credited. The backfill decision is extracted to a pure, unit-tested backfillStartSlot.

Skywire: Unwedging the TinyGo wasm-visor, and v1.3.91

3620 fix(appserver): reflection-free gob RPC on js (unwedge the TinyGo wasm-visor) fixes the TinyGo variant wedging inside the HV SharedWorker (the Go variant booted fine in the same worker, and the same TinyGo blob booted on the main thread — so it was TinyGo-in-worker specific). Captured live via CDP, the root cause was panic: unimplemented: (reflect.Type).Method(): gobrpc already ships a reflection-free RPC server for TinyGo, but the app-server’s in-process RPC still called RegisterName(gateway), the reflection path, which TinyGo’s runtime reflect can’t service — so the moment the visor launched an in-process app (skychat) it panicked and trapped the goroutine, wedging the worker. The fix build-tags the in-process registration: native keeps RegisterName; TinyGo registers each of the gateway’s ~18 methods as a reflection-free gobrpc.HandleFunc, wire-identical so the app-side client is unchanged. 3622 chore(release): v1.3.91 — re-embed wasm blobs (TinyGo boot fix) + changelog cuts the release, re-embedding both committed wasm-visor blobs from develop HEAD so the browser wasm-visor ships the #3620 fix — verified live that the TinyGo variant now boots in the HV SharedWorker and runs its in-process skychat, where it was previously wedged.

Skywire: The Wallet’s BTC Backend Moves Home

3621 feat(wallet): BTC electrum server lives in the wallet’s Settings → Nodes (only skysocks stays visor-side) fixes an inconsistency in where the Bitcoin electrum server is configured. The skycoin-web wallet already models Bitcoin’s electrum server as the Bitcoin coin’s node URL, editable in Settings → Nodes like every other coin — but the /wallet fetch shims ignored it and pulled the electrum backend from a separate HV-overlay field, so setting BTC’s node in the wallet was a no-op and the config lived awkwardly on the visor side. Both shims (native HV and the hv serve wasm-PWA host) now source the electrum server from the wallet’s own customNodeUrls, falling back to the legacy field and then a public default, and the config overlay drops the electrum field entirely — keeping only the skysocks exit, the one thing that genuinely belongs on the visor side. No wallet-blob or Angular rebuild was needed.

Skywire: A Deterministic Route Fallback

3623 fix(skyroute): deterministic ErrNoMux via yamux Ping liveness probe fixes a test that was flaky under -race on develop — and a real correctness bug behind it. openWithTimeout probed mux liveness with yamux.Session.Open(), which returns a stream optimistically, before the recv loop notices the far end is dead; against a destination not serving the mux forwarding port it races, sometimes handing back a dead stream with err == nil so ErrNoMux (and the negative-cache entry that lets callers fall back cheaply) never happens. In production that hands a caller a dead mux stream instead of falling back to a 1:1 dial. The fix probes with a yamux round-trip (Session.Ping), which the far end auto-answers, so a non-mux destination fails deterministically — fast on a dead pipe, bounded by the existing timeout on a dead route, one extra round-trip only on a cold open of a live mux.