Back to list
Skywire Development Update — October 4, 2026
The fourth of October shipped v1.3.99, led for the fleet by a fix for visors that were stuck before they ever finished starting, walking a corrupt stats database forever. The browser visor kept closing the distance to a native one: it now hosts interactive shell sessions through websh, its dashboard terminal works, skynet port forwarding reaches the tab’s own virtual network in both directions, and the Skycoin wallet runs inside it as a real visor app with its wallet files on the tab’s disk, Bitcoin included, reached through the tab’s own proxy. The dashboard gained a Mail tab for every visor, a single proxy port now answers both clearnet and mesh names, the native end-to-end suite finally stopped routing over dmsg and found a real Windows VPN bug on the way, and the Android core was brought back under its size budget.
Skywire: v1.3.99
5397 released v1.3.99, with a changelog covering pull requests 5280 through 5394 and the js/wasm command module re-embedded from a clean tree so that go install builds serve a current module. Its highlights for the fleet are the bbolt fix below, the Windows VPN short write, the yamux receive lock fix behind slow standby recovery, and the max_share race in route spreading. 5400 fixed a skychat history test that failed on the release run itself: the per-peer limiter counts per calendar minute, and a burst that crossed a minute boundary was legitimately allowed up to twice the cap, so the test now waits out the last few seconds of a minute before sending its burst.
5390 fixed some fleet visors holding between half a gigabyte and 1.4 GB of heap and never finishing their start. On the affected visors, most of the live heap was the cursor stack inside the stats database’s integrity walk, which was still running: a corrupt branch page pointed back up its own path, so the cursor descended forever. The bbolt fork now panics past sixty-four levels, the integrity probe already turns a panic into a corruption verdict and moves the file aside, and the fork’s new test builds exactly such a self-referencing page. Affected visors pick this up on their next update and restart. 5401 followed with a second bbolt fix, for js: the emulated mmap is refreshed only on fdatasync, so databases opened with NoSync read stale pages and panicked on the next write, and NoSync is now ignored there.
5389 fixed the route-spreading case where one route sometimes finished with half an object against a 0.4 share cap. Two concurrent picks raced the promotion of a standby route, and the pick that saw nothing to promote kept a stale two-route list where both routes looked capped, so the never-stall rule handed the chunk to the faster one. A short candidate set is now re-read even when nothing was promoted, and the cap is measured against every byte the object has placed, including bytes on routes that have since left. The test passed 240 of 240 runs on the fake clock and 24 of 24 on the real one, where it had failed about one in forty and one in twelve.
Skywire: A Shell, a Terminal and Forwarded Ports in the Tab
5396 let a browser visor host interactive pty sessions. A tab has no pseudo-terminal, so it had answered pty exec and served files over sftp but refused a shell; it now runs a websh session in its place, with the remote terminal’s input going to websh’s line editor or the running command’s stdin and the shell seeing the same filesystem sftp serves. From a native visor, skywire cli pty start into the tab gave a prompt, ran echo, ls / and skywire cli visor pk, and ended on exit.
5405 made the dashboard’s Terminal tab work on a browser visor, where it had been hidden. Three things stood in its way: the websocket library’s accept is a stub under js, a service worker cannot carry a websocket, and the socket’s data arrived from the desk page’s realm so the terminal’s own type check failed. The pty UI now accepts through a small wrapper that uses the x/net websocket under js, the bottle fork adds a websocket client over a vnet connection that builds its data in the caller’s realm, and the vnet worker routes /vnet/ websockets through it. Typing skywire cli visor pk into the tab’s dashboard terminal printed the tab’s key.
5394 fixed skynet forwarding and serve add in a browser visor reaching their local side through plain net.Listen and net.Dial, which never touch the page’s virtual loopback, so a port served from the tab returned 404 and a port forwarded into it refused connections. They now use bottle’s vnet, which is plain net on a native visor. Tested live, a dashboard published from the tab answered 200 from a native visor over both .dmsg and .skynet, and a native HTTP server forwarded into the tab answered 200 on its vnet port.
5395 raised the desk’s vnet service worker timeout to 35 seconds, just above the page’s own 30, after the desk’s dashboard showed “problem updating the data”: the visor tree summary took about sixteen seconds when the desk managed a native visor that is itself a hypervisor, and the worker gave up at eight. The slow case did not recur during testing, so the fix was not reproduced against the error. 5403 made a /vnet/ page survive a hard reload, which bypasses service workers and had reached the server as a bare 404; the server now answers with a small page that reloads once normally through the worker, says to open the desk if no worker is registered, and stops rather than loops on a second visit within five seconds.
Skywire: The Wallet Runs in the Browser Visor
5410 made skycoin-web run in-process in the browser visor as it already did natively, so a tab’s wallet stopped keeping its keys in localStorage. While it runs, the dashboard’s Wallet tab uses its server-side wallet files, which in a tab live under /opt/skywire/wallets and persist across reloads, and it opens no port. The same change fixed two bugs a native visor shared: running the app’s command started skywire’s whole command tree from the root a second time, and the app never opened an app client, so starting and stopping it hung. The resolving proxy is now passed as a flag rather than HTTP_PROXY, which had been set for the whole visor process, and a config regen keeps an app’s environment along with its arguments. The native in-process path was covered by unit tests only.
Three wallet-side changes made that possible. skycoin#3084 made skycoin-web build for js by taking its bbolt imports from the same fork skywire uses, and let an embedder receive the wallet handler and open no port, with a server error returned instead of exiting the host process. skycoin#3085 stopped --socks5-proxy from setting the proxy environment for the whole process, giving node requests their own proxied transport instead, and turned a wallet directory that fails to open into an error rather than a fatal exit. skycoin#3086 made that proxy work in a browser, where Go’s HTTP client falls back to fetch() and ignores the proxy unless it has a dialer of its own, and retried an unreachable node’s health check every fifteen seconds so a node that was not up at startup is found later.
5411 turned Bitcoin on by default when a resolving proxy is running, which is always the case in a browser visor, through the wallet’s built-in electrum servers reached through the proxy and so through the exit. Restarting skycoin-web exposed three more bugs fixed there: an exiting app’s environment cleanup could unset the config a restarting instance had not read yet and take the visor down, the old run’s unmount removed the new run’s handler, and a restart appended a node URL instead of replacing it. Three restarts now leave the visor running, and the Wallet tab showed Bitcoin with electrum connected. skycoin#3087 supplied those defaults, a list of electrum servers tried in a fixed order with failover, connected on first use and reached through a small SOCKS5 client, keeping the user’s IP from the electrum server natively and making it reachable at all from a tab. skycoin#3088 made the Bitcoin node status answer, since the wallet checked a health path skycoin-web did not serve and always showed “electrum unreachable” while balances and fees worked.
Skywire: Mail in the Dashboard, and One Proxy Port
5402 added a Mail tab to the hypervisor dashboard beside Chat, backed by the visor mail API now served for any visor including remote ones: addresses and state, inbox and sent, reading with attachments, reply, compose, delete, the on and off switch and the whitelist, with attachments served as downloads. 5404 fixed two CI lanes that change broke, marking the mail component’s async views for change detection and stubbing the mail routes out of the mobile build, which has no dashboard and had gone 294 bytes over its size budget.
5388 made one proxy port answer mesh names as well as the clearnet. A browser that wanted both had to be pointed at a chain of localhost proxies, paying two extra SOCKS handshakes on every clearnet request. The visor now publishes each resolving proxy as a local service that an app dialing its own visor on that port is served in-process, and skysocks-client hands a matching name straight to it. This deliberately avoids the registry that incoming peer connections are dispatched from, since a resolver reachable that way would be an open proxy for this visor’s alias table, certificate authority and exit. The SOCKS handshake now happens before the exit is chosen, so a mesh name resolves with the exit down or with none configured at all. The separate resolver listeners are untouched; the PR notes the live check has not been run yet.
Skywire: End-to-End Tests and a Windows VPN Bug
5391 stopped the native end-to-end suite routing skysocks and VPN over a dmsg transport, which took minutes and often never finished because the address resolver refuses a runner’s private IPs; the tests now add an stcp transport straight to the other visor. A hypervisor database path that pointed at one developer’s machine, which made the visor exit ten seconds after it was ready behind a relaunch loop, now sits in the work directory, and the ninety-second warm-up and the relaunch loop are gone. On Linux the full suite passed six runs out of six at 46 seconds each with no retries. The remaining Windows VPN failures had a real cause: the WireGuard TUN device’s write returns a packet count, the Windows wrapper passed it on as a byte count, and the copy stopped with “short write” on the first packet back, so the client died a quarter second after reaching Running. The wrapper now returns the buffer length, and the test checks the client is still running ten seconds later. The Windows and macOS lanes were verified only by CI.
Skywire: Observability, Mobile Size, and Dependencies
5385 gave native visors Go 1.27’s execution trace flight recorder, snapshotted to the log directory when the router first sees a reorder wedge and served over pprof alongside the new goroutine leak profile, both fetchable with cli log pprof; it had not yet run on a live visor. 5407 brought the Android core back under its budget after it grew from 68.8 MB to 73.4 MB, mostly from the embedded transport discovery, address resolver, service discovery and route finder services and their Redis client being linked into the phone build. Their registration imports now build only without the mobile tag, saving 3.9 MB, with some deployment packages still linked through other paths as a larger follow-up.
5393 switched skywire cli wisp and the visor’s embedded wisp server to the extracted 0magnet/wisp module and deleted the in-tree copy, adding a slog handler to skywire’s logger that forwards to logrus at the configured level. 5398 bumped the metrics, gotop, sh and wisp forks, adjusting the desk console for the shell fork’s context-taking job stop and its rule that background jobs outlive their line only in an interactive runner. 5387 bumped the dashboard’s Angular build package and rebuilt the bundle.
Skywire: Tours and the README
5409 reworked the desk tour: its launcher step opens the application menu, the browser step opens a page showing the exit’s IP address, the dashboard and mail steps open the dashboard, and the last step hands off to the dashboard’s own tour, which gains Mail and Terminal steps. The desk’s mail entry now opens the dashboard’s Mail tab, since mail is a visor app, and the separate desk mail pane is gone. 5386 restructured the README, splitting the wide comparison tables that overflowed on GitHub, adding a quick start and contents line, regenerating the dependency graph and line counts, and fixing stale command names and broken links. 5392 added a section on ideas borrowed from other projects, mostly for route multiplexing, such as BitTorrent’s endgame and pipelining in skysocks and BBR’s minimum RTT in the router’s legs, each linked to the file that adapts it. 5406 added a table comparing the native visor, the browser-tab visor and the Android app across transports, exits, VPN, hosting, apps, pty, wallet, background running and rewards.