Back to list
Skywire Development Update — September 30, 2026
The thirtieth continued the previous day’s push through transport discovery’s Redis load with a dozen more targeted fixes, each one measured against the same production host and each shaving off a specific, named cost rather than restructuring anything wholesale. In the browser desk, real-origin mesh and clearnet browsing picked up connection reuse, cookies, streamed responses and a relayed WebSocket, closing most of the remaining gap between browsing inside a tab and browsing anywhere else. The hypervisor UI’s network-wide transports tab, which had been freezing the browser outright on a large fleet, was rebuilt to summarize rather than ship the whole mesh. v1.3.98 shipped, and the repository gained its first AGENTS.md, combining the AI-contribution policies of the upstream projects skywire itself depends on.
Skywire: Registering a Transport Only Once
5283 found transport discovery rewriting every transport in a visor’s reported list in full, nine Redis commands apiece, purely to keep its five-minute expiry from lapsing — roughly eight hundred full rewrites a second of data that had not actually changed on one production host. A transport that is new or genuinely different is still written in full; an unchanged one just has its lifetime extended with a single batched command instead. 5282 found the reconcile step that was supposed to detect “unchanged” doing it wrong in a specific way: each edge of a transport labels it differently from its own side, so the acceptor’s and the dialer’s snapshots of the identical transport looked like a change to each other every single time, triggering a full nine-command re-registration on every report; the fingerprint used to detect a real change now ignores the label each edge applies for itself.
5292 found every service restart re-registering the entire mesh from scratch because the change-detection memo always starts out empty, now seeded directly from whatever the store already holds so an unchanged transport’s first report after a restart costs one command instead of nine.
5286 gave transport discovery’s own store an in-memory copy of the full transport set, since reading it back from Redis as an index lookup plus a full fetch of every entry had been costing roughly eighty thousand transports’ worth of round trips on every single one of several different one-minute and fifteen-second cycles; the store now keeps that set current as it writes, loaded once at startup and aged out on the same expiry the stored keys already use.
5289 and 5296 both built directly on that in-memory set: reading a specific transport by its endpoints now answers from an index kept beside it instead of a fresh lookup, and a visor’s reported transport list is now decoded from JSON only when its underlying bytes have actually changed rather than on every single heartbeat. 5295 found a dial-back to a visor triggering a full reconciliation pass that asked every one of roughly nine hundred held connections whether it was subscribed, when only the one connection that was just dialed actually needed to be; it now subscribes that connection directly, leaving the periodic reconcile to catch anything genuinely missed.
5278 found that even a two-minute cap on how often a transport’s telemetry gets applied still left nearly two thousand ingest operations running every second, since every live transport’s byte counters genuinely do move within that window; the window widened to five minutes, costing nothing since the counters are cumulative and a wider window just means a bigger delta next time.
5291 found the feed publishing a full day’s metrics re-encoding and re-compressing that entire day on every sixty-second tick regardless of whether new data had actually arrived, accounting for roughly six percent of one host’s CPU; it now only republishes once every five minutes, in step with how often telemetry can actually change under the new pacing. 5290 found the route finder’s internal graph discarding its landmark lookup tables on every fifteen-second rebuild and paying to rebuild them from scratch on the very next query, when the hubs those tables describe change slowly enough to reuse them for up to two minutes.
5277 fixed the uptime database failing to open at all in a freshly created container because nothing had ever created the directory its file lives in, silently recording no uptime data on every fresh deployment until someone noticed.
Skywire: Real-Origin Browsing Gets Faster and More Complete
5300 found every proxied browse request inside the desk opening a brand-new connection with no reuse at all, so a page with several subresources paid a fresh proxy stream, TCP handshake and TLS negotiation for each one — six to nineteen seconds per request before the fix, six hundred and forty-nine milliseconds after it, once requests through one browse domain started sharing a bounded, kept-alive connection pool. The same change gave the browser a single cookie jar so a server’s session cookie actually survives between requests, and stripped WebRTC and WebTransport out of a real-origin frame entirely, since neither one can pass through the service worker that makes real-origin browsing possible in the first place and a STUN query through either was simply leaking the reader’s own IP address back out.
5307 replaced a browse response’s full buffering in memory, capped at sixteen megabytes and silently truncating anything larger, with a proper stream straight from the upstream into the frame, verified moving twenty megabytes of a hundred-megabit test file at a sustained read rate the sixteen-megabyte cap could never have allowed; the same change gave status.skysocks a real browsing origin instead of the sandboxed transcoder, letting its own status WebSocket actually reach it.
5308 closed the remaining cookie gap: a service worker never sees the Cookie header on a request or Set-Cookie on a response it builds, so non-HttpOnly cookies a server set never reached a page’s own script and cookies the page itself set with document.cookie never reached the server; the framed page now carries its own cookies along explicitly in both directions, limited strictly to its own site.
5302 gave a real-origin frame’s WebSocket connections somewhere to go other than straight to the network from the reader’s own address, which is what every single one of them had been doing since a service worker simply never sees a WebSocket at all; a stand-in implementation now hands each one to the desk, which opens the real connection through the browse proxy and relays messages in both directions — verified against a public echo server with every other mechanism, fetches, forms, redirects and cookies, still exiting correctly from the skysocks exit’s own address.
5299 fixed sites that specifically refuse to load inside any frame unless document.referrer names their own domain, by having the transport report each response’s real address and the page’s own bootstrap present that as the referrer, exactly as it would read after an ordinary same-site navigation. 5298 fixed status.skysocks never updating at all when rendered inside netscrape’s sandboxed frame, which cannot hold the status page’s own WebSocket connection; it now polls a plain fragment endpoint instead whenever the socket can’t open, and stops polling the moment a socket does.
Skywire: CLI and Autoconnect Learn What a Browser Visor Can Actually Dial
5301 and 5303 fixed tp add pv refusing every transport type except two hard-coded ones and defaulting to a type a browser tab cannot open at all, which meant the command simply didn’t work from the wasm visor; it now validates against the live transport-type registry directly; so a newly added type is automatically accepted with no command-side change required, and defaults to a type a browser tab can actually use. 5306 found a browser visor’s own autoconnect picking fifteen random public visors for its WebTransport phase when the address resolver’s own reachability feed already knows that the specific protocol is open on barely two dozen of them and closed on roughly eight hundred — every public visor the browser doesn’t yet hold a transport to is now a candidate, dialed in order of confirmed reachability rather than at random, which took one tab from a measured fifty-five to two-hundred-fifty-one seconds before its proxy came up to a consistent fifty-eight.
Skywire: A Transports Tab That Stops Freezing the Browser
5254 found the hypervisor UI’s mesh-wide Transports tab capable of freezing a browser’s main thread for over five minutes without recovering, tracing it to transport discovery’s metrics endpoint being a whole-mesh dump of over a hundred and eighty thousand records and sixty megabytes of JSON that the hypervisor had been proxying through verbatim — a response that routinely got cut off by its own server-side write timeout partway through, handed to the browser as a nominally successful response full of unparseable JSON, which the table then tried to render every row of regardless. The fold the UI actually needed, two bandwidth totals per record sorted by size, now happens in the proxy itself, returning only the top two thousand records with the real total alongside, decoded element by element so a truncated upstream response still yields whatever records did arrive rather than failing outright; the same fix measured the response size dropping from over sixty megabytes to six hundred and seven kilobytes, with the page loading in under five seconds against never finishing before.
Skywire: Smaller Fixes Across the Tree
5287 found the noise handshake’s Diffie-Hellman cache memoizing every computation, static and ephemeral alike, when only one step — the static-to-static exchange — actually repeats between the same two peers; the ephemeral computations had been cycling a four-thousand-entry cache every few seconds on the address resolver specifically, evicting the one entry worth keeping before the same peer ever came back, and a redundant second validation of the peer’s key was dropped along with it.
5305 fixed a malformed edge pair in a route-finder request panicking the handler outright with an empty 500 response instead of simply refusing the request with a 400, caught live on a production host the same day. 5304 fixed a background terminal tab in the desk losing its scrollback the moment it was hidden, because the terminal’s own resize observer fit it down to a zero-by-zero grid while invisible, rewrapping and discarding every line in the process.
5238 gave the desk’s own shell session the same job-control reporting bash has always had — a finished background job now actually prints its completion line and is removed from the job table, rather than going silently quiet while sitting in that table for the rest of the tab’s life, which also fixed job numbers staying stable instead of drifting every time an old, finished job was still occupying a slot. 5285 let one block in a multi-service config file take its signing key from its own file rather than requiring it inline, needed specifically to let several deployment services share one container while each keeps its key in its own separate secret.
5281 gave cli sd’s transport-type breakdown a column for every known type instead of silently folding four of them into an undifferentiated total, which had been making roughly six hundred squicr transports per public relay invisible.
5280 fixed the TinyGo-built config generator page failing to compile against the day’s own tree for three unrelated reasons — a function call with no TinyGo-side implementation, an import TinyGo’s toolchain can’t build under the Go version in use, and a JSON library whose reflection internals TinyGo doesn’t support — each one fixed in isolation so the apt-mirror publishing pipeline, which depends on that page building, could unblock.
Skywire: A Release, a Dependency Sweep, and an Agents Policy
5279 shipped v1.3.98, with its changelog covering everything since v1.3.97’s cutoff and the embedded wasm module re-staged from a clean tree so every fresh install serves a module that reports as built from unmodified sources. 5297 fixed the hypervisor UI’s bundle going stale after an automated dependency bump had moved one Angular package without its siblings, bringing the whole framework to one consistent version and rebuilding the committed bundle to match. 5255 merged a routine dependency bump to a JavaScript IP-address parsing library. 5288 added the repository’s first AGENTS.md, combining the AI-contribution policies of the upstream projects skywire itself depends on or contributes to into one standard for any AI agent working in this codebase — plain language, short comments, no tool-attribution trailers in commits, and always running a change before submitting it, alongside the project’s own existing rules like building only the root package and never a replace directive in go.mod.