Back to list
Development Update — August 8
Mostly reliability work under the hood. A silent first-session publish skip that kept a freshly-booted visor out of dmsg discovery — and blew the e2e suite’s time budget waiting for it — was tracked down and fixed. The CXO memory-growth and CPU-saturation seen on the live deployment hosts got three targeted fixes, paired with a server-side change that keeps the SD services feed always servable. On the UI side, the VPN client became a first-class in-place node tab and the clearnet iframe browser gained real reliability and browser parity.
Skywire: A First-Session Publish, Silently Skipped
Nine e2e tests were failing on the same precondition — “visor visor-a did not appear in DMSG discovery with delegated servers within 2m0s” — and the suite then blew its 45-minute budget waiting. The visors were healthy; they simply weren’t in discovery. fix(dmsg): publish the first client entry even when the read shows no delta is the fix. updateClientEntry reads the current entry and writes only on a delta, but a visor’s disc client is a registering fallback whose reads resolve direct-first, and the visor seeds its own PK into that direct client — where GetAllEntries synthesizes a self-entry listing every configured server as delegated. So the read describes local config, not what dmsg-discovery holds; with a single dmsg server (every e2e run) that synthetic set equals the live session set, the delta is empty on the very first publish, and the client stays unregistered until the periodic tick one UpdateInterval later. In the log, visor-a boots at 08:58:42 and its first “Updating entry.” is at 09:03:43 with nothing in between — five minutes undialable. updateClientEntry now forces the write when it has never published and has a session to announce (nil lastPushedSrvPKs is the established “never published here” signal, which SetDiscoveryClients already clears for the same reason), while steady-state suppression is unchanged so reconnect storms stay off the discovery. The e2e freshness bound moves with it — a flat 60s was calibrated to the server’s default interval and rejected a good entry four minutes out of five against the client’s own five-minute cadence, so it now derives from that cadence, and the restart test pins its cutoff to the restart itself rather than “now − 60s”, which had been landing before the restart it was verifying. A test pins the invariant and fails without the fix.
Skywire: CXO Memory, CPU, and the SD Publisher
3798 fix(cxo): TPD aggregator feed leak + dmsg-disc encode-cache + shared conn reaper bundles three independent fixes for growth seen on the live hosts (diagnosed via heap/goroutine profiles over the resolving proxy). First, the transport-discovery aggregator subscribed to every visor’s feed but never released it — RemoveRootObjects(c,1) keeps each feed’s last Root forever, so a feed whose visor has gone away (notably an ephemeral wasm visor with a one-shot PK) pinned its final Root tree in the in-memory CXDS permanently; live heap climbed 1128→1233 MB in 12 min (~+500 MB/hr) with goroutines 2445→4021. cleanup() now reclaims feeds with no connected conn (DontShare then DelFeed), grace-gated so a brief drop-and-redial keeps its feed. Second, the treestore encode-cache never persisted under dmsg-discovery’s ~160 registrations/s because a mutation always landed mid-encode and the whole-tree promotion was discarded every cycle, forcing a full re-serialize of the clients-by-server tree — the allocation storm behind dmsg-discovery’s GC-bound CPU (~61% runtime.scanObject); per-subtree mutSeq stamping now re-caches only the subtrees that weren’t touched mid-encode. Third, every CXO Conn spawned its own idle-watchdog goroutine and ticker — hundreds on a ~400-conn aggregator — replaced by one shared Node.connReaper walking active conns on the same interval, with detection latency unchanged.
3800 fix(sd): periodically republish the services CXO feed is the server-side companion to yesterday’s #3799. The SD host and its CXO publisher were healthy, but the services publisher emitted a Root only reactively — on register/deregister plus a one-shot startup warm — so during a content-idle window, or after a cold-redis start whose warm found nothing, LastRoot returned nothing (or a Root whose objects the cleanup sweep had since reclaimed), and subscribers timed out and fell back to dmsg-http (6/6 cxo refresh sd-services over 2 min timed out). It now re-warms the tree from the store every 60s, the same cadence the TPD uptime/metrics/all-transports publishers already run, republishing a fresh, fully object-backed head Root and self-healing a cold-start miss. Where #3799 made the client wait correctly, this makes the SD publisher actually have something to serve.
Skywire: In-Place App GUIs and the Clearnet Browser
3794 feat(hv-ui): in-place app GUIs — VPN tab, unified full-page app host is phase 1 of surfacing the proxy/VPN control surfaces in-place, toward the proxy-switcher work. The VPN client UI was only reachable via a button that opened it in a separate browser window; it’s now a first-class in-place node tab like skychat and the terminal, mirroring the terminal-tab pattern — a vpn node child route, NodeComponent iframing /#/vpn/<pk>/status as a sibling kept mounted across tab switches, with an “Open in new window” button preserving the old launch. Iframe-hosted-tab detection was made segment-based (selectedSeg) instead of a hardcoded index so inserting the VPN tab can’t desync the terminal tab, and the tab is hidden on the wasm visor (host-only).
3788 feat(wasm-visor): clearnet iframe browser — reliability + browser parity makes the WinBox iframe proxy browser render regular clearnet sites reliably over skysocks-client-lite. jsFetchClearnet now loops over exits instead of failing on the first bad one, rotating away from a dead exit (up to 4 tries in auto mode) with a 12s ResponseHeaderTimeout to fail a dead egress fast and a 120s body timeout so large pages complete; opaque-origin shims give self-contained WASM apps an in-memory localStorage/sessionStorage and swallow the history.replaceState errors that throw in an about:srcdoc origin; the visited site’s favicon renders in the title bar, fetched over the same channel the page came in on; and every clearnet fetch now forwards the host browser’s real navigator.userAgent, so a proxied fetch is indistinguishable from a normal tab. Live: chaosrack (a 9.4MB Go/WASM app) loads and animates at 60fps with zero “Go program has already exited” errors (previously 700+), en.wikipedia.org went 403→200, and duckduckgo.com’s robot wall became real HTML — the last two purely from the User-Agent fix. Runtime-built JavaScript SPAs still render mostly empty (the clearnet render is static, scripts stripped); running scripts and proxying their runtime fetches through the exit is a follow-up.
Skywire: Dependencies
3802 chore(deps): batch open Dependabot bumps folds the open CI-action and npm-lockfile bumps into one merge, replicating the Dependabot PRs on the fork branch as usual.