Back to list
Development Update — August 4
The day’s most important change is a security fix in the shared httpauth middleware: over dmsg, the caller’s identity is now taken from the noise-verified session PK instead of an unverified header, closing an impersonation hole that let any dmsg peer spoof another visor to the address-resolver, transport discovery, service discovery, and the uptime tracker — and it reduces load while doing it. Alongside it, the hypervisor SPA’s long-planned GUI-embedding standardization landed in four steps (a SharedModule, a generic bundle-mount host, the first lazy-loaded feature module, and CDK-portal mounting that retires the self-iframe), the wasm-visor’s runtime-config reached parity with native and became editable, the clearnet browser got its search/nav/CSS working over the proxy, and the Android app grew SkyDEX, voice calls, and a shared address book.
Skywire: Authenticating dmsg by the Session PK
3725 fix(httpauth): authenticate dmsg requests by the noise-verified RemoteAddr PK closes a real impersonation hole in the middleware shared by AR, TPD, SD, and the uptime tracker. Over dmsg the caller PK is authenticated by the noise-KK handshake and carried in RemoteAddr, and the auth path had a dmsg optimization that skipped the expensive secp256k1 signature — but it still acted on the unverified SW-Public header, and nothing bound that header to the dmsg session PK. Since the nonce is public (/security/nonces/<pk> is unauthenticated), the signature was the only real proof, and it was skipped — so any dmsg peer could impersonate any visor by forging SW-Public, e.g. POST /bind/wt as another visor to spoof an AR transport record and redirect traffic, with the same surface defeating TPD’s register/report and SD’s PKFromCtx == entry.PubKey guard. The fix takes the authenticated identity from the noise-verified RemoteAddr PK and skips both the signature and the SW-Nonce (both redundant over an ordered, replay-proof, authenticated stream); plain-HTTP requests are unchanged (full nonce + signature). One change in shared httpauth fixes all four services, and it’s load-reducing — it avoids re-adding the secp256k1 work and eliminates the SW-Nonce-desync 401→refetch→retry churn that concurrent clients (especially the browser wasm visor racing the monotonic per-key nonce) generate. Two related fixes cut deployment noise: 3727 makes the address-resolver return 404 not 500 for /resolve/<type>/<pk> of an unresolvable type — webrtc dials by PK over dmsg signaling and has no AR record, so the redis store’s ErrUnknownTransportType was mapped to a 500, logging ~750 spurious server-faults a run (from older fleet visors) and inflating the error-rate metric alerting keys on. 3732 raises the PR-addressable OpenSSF Scorecard checks — a SECURITY.md disclosure policy, least-privilege permissions: on the six workflows that had none, a CodeQL SAST workflow, and Dependabot for GitHub Actions — and 3728 adds an operator guide for making a visor reachable from the internet and troubleshooting when it isn’t (the four factors: is_public, a stable transport_port, TCP+UDP port-forward, TCP+UDP firewall; the common failure is TCP transports working while UDP ones fail with no recent network activity because the UDP rule is missing — and since WT is the carrier browsers dial, UDP reachability is browser reachability).
Skywire: GUI Embedding, Standardized
The hypervisor SPA — also re-served as the wasm-visor UI — declared all ~169 components in one monolithic AppModule with no SharedModule, no feature modules, and every route eager, which blocked splitting any feature out. Four PRs execute the RFC from #3706. 3715 extracts a SharedModule declaring the 22 cross-feature layout components (button, dialog, top-bar, paginator, line-chart, …), the autoScale pipe and the clipboard directive, and re-exporting them plus CommonModule/forms/the common Material list — while keeping RouterModule.forRoot and TranslateModule.forRoot root-only so future lazy modules share the single router and translate service. 3716 factors the external-bundle mount machinery out of the network-visualizer into a reusable <app-bundle-mount> host that loads a first-party bundle once, resolves the mount global by dot-path name, calls the mount(root, opts) contract into its own <div>, tears it down on destroy, and renders loading/unavailable/error states. 3717 is the first lazy conversion: the eight VPN components move into a lazy-loaded VpnModule + VpnRoutingModule and /vpn switches to loadChildren (the guard’s canActivate stays on the parent so it runs before the chunk is fetched), shipping VPN as a separate ~100 KB chunk out of main.js. 3720 finishes the series by replacing the wasm desktop’s ☰ Chat and ☰ Logs WinBox windows — which had iframed the SPA back into itself, loading a second Angular runtime to show one component — with an in-context mount via a new NgBridgeService (window.SkywireNg.mountComponent) that creates the real component through a CDK DomPortalOutlet in the app’s root injector; the key finding was that both components only read node.localPk and inject no ActivatedRoute/Router/NodeComponent, so an optional embeddedNodeKey input synthesizes the node from the key and routed behaviour stays 100% unchanged, with browse.js falling back to the old ?embed=1 iframe if the bridge is absent. Net: one Angular runtime, shared context, no self-iframe, and no dual-surface divergence of the 1500-line skychat component.
Skywire: wasm-visor Config Parity and Log Forwarding
3726 feat(wasm-visor): mirror the full native config shape in the runtime-config view fixes a /runtime-config that exposed only four service URLs, looking nothing like native config gen; it now mirrors the native visorconfig.V1 shape section-for-section — populated where applicable (dmsg, transport including address_resolver/public_autoconnect, routing including min_hops/route_setup_nodes, launcher with service_discovery + the skychat/skysocks-client-lite apps, stun_servers, reward_system) and no-op stubs with a platform_omitted reason for what a browser edge can’t have (pty, raw sockets, local HTTP/db, disk, and sk). It’s hand-built rather than marshaled because visorconfig.V1 transitively pulls modernc.org/libc, whose build constraints exclude js/wasm. 3738 takes it to full parity: wasm config becomes runtime-reconfigurable (a browser edge has no on-disk config, so overrides persist page-side in localStorage, pass at boot, and merge onto the resolved deployment services), the native GetRuntimeConfig redacts sk while SetRuntimeConfig re-injects the real key on save and rejects any PK change, and the HV-UI runtime-config editor’s PUT — which had 404’d on the browser edge — is now handled by the wasm core so the same editor Saves on both surfaces. 3724 feat(wasm-visor/harness): forward WARN+ subsystem logs to the /ctl/log shell bridge fixes a diagnosis blind spot: the wasm visor’s route/transport/dmsg/skysocks failures were only in the in-tab log ring and the browser console, not the shell-readable /ctl/log bridge that hv serve --harness mounts; vlogHook.Fire now mirrors WARN/ERROR/FATAL/PANIC from every subsystem out through the __skylog bridge (gated to warn+ and to the bridge’s presence, so inert in production), and it immediately surfaced that clearnet browsing on a fresh keyless harness was flaky because of transport availability — with no transport the route finder can’t reach any exit — not dead SD exits.
Skywire: Clearnet Browser and Visualizer Polish
3723 fix(skynet-browser): search forms, links, CSS images + legibility on proxied clearnet pages fixes a batch of breakage reported from the WinBox iframe browser over skysocks-client-lite: a search box did nothing, in-page links didn’t navigate, CSS-referenced images didn’t load, and unstyled pages rendered dark and illegible. The clearnet render inlined resources under a strict CSP but — unlike the dmsg render — injected no nav/form shim, didn’t rewrite url() refs in CSS, and set no canvas default. browse.js now injects a clearnet nav/form shim (link clicks and form submits resolve to an absolute URL and route through the parent proxy — GET → action?query, POST → body, restoring search and navigation without the page’s stripped JS), rewrites url() in fetched stylesheets and inline <style> through the same skysocks exit, and sets a light canvas default injected first so the page’s own CSS still overrides — verified by fetching a real google.com/search?q=… through a skysocks exit (HTTP 200, results mention “skywire”) and running the form-submit chain end-to-end. A run of tpviz WebGL work brought the cosmos renderer to Flat-view parity — version filter, search focus, persistent selection and physics/tooltip toggles (batch 1), show-services/show-routes recolor (batch 2), DMSG-server nodes and edges (batch 3), country bubbles sized by count that hug their nodes, and stopping the boundary overlay redrawing every idle frame — with the visualizer bundle and /api/* now served for the wasm visor and degrading gracefully when tpviz isn’t served. A router follow-up (799e305c8) tightened the #3653 raced route-setup: on a cold dial the background transport-creation hook lands a direct transport in ~3-4s, but the route-finder query blocks on its full ~10s HTTP-over-dmsg timeout and can only ever return a multihop path, so the fresh direct transport sat unused until the finder timed out; the router now probes in-memory for a direct transport when the hook completes (the cheap directRoute half of local-route calc, no bulk GetAllTransports) and uses the 1-hop route immediately, dropping skysocks-client route setup on restart from ~12s to ~5s.
Skywire: The Phone Grows Up
The Android app filled out its app surface and gained voice. SkyDEX came to the phone — the market key entered natively with the trading UI embedded, and the SkyDEX tables re-laid as cards with a Cancel button that actually works. Calls got audio, a call screen, and a log, with the screen shown for outgoing calls and reachable on every tab, and message/call notifications wired in. The address book moved to the visor so names reach every surface, the phone became a generic sink for the notification hub (the same hv notify-style bridge), and skychat:// deep-links now open on the phone with voice calls reachable through them.